Todavía me estoy haciendo a la idea de que existen esos nuevos artilugios infernales llamados genéricos y anónimos y ayer, por vez primera creo que busqué profundizar con más detalle en estós últimos. Reconozco que mi capacidad para imaginar qué pueden hacer y qué pueden aportarme anda bajo mínimos (siempre estuvo así, pero ahora quizás con más razón) y me resulta chocante descubrir nuevas lineas de código, que intento descifrar con la misma naturalidad que sentimos al fijar la vista sobre aquello que se nos muestra turbio.
Así que uno va leyendo y acumulando ideas con la esperanza de que en algun momento se aclare esa visión y vea nítidamente la panorámica y el escenario.
En esas pesquisas, descubría un enlace que me pareció muy interesante y que hablaba de los métodos anónimos, y lo que es mejor, razonaba sobre su comportamiento y sobre como, desde ellos, se accede a las variables que caen fuera de propio ambito. En este caso concreto, el metodo accede a una variable local al procedimiento que lo invoca. El experimento resulta muy curioso.
Está era la conclusión:
Aha! So it’s not stored on the stack after all; it’s treated more or less as a var parameter referring to a field inside the closure object. It’s referenced as [esi + $0C], so our “local” variable is stored 12 bytes in, which is right where we’d expect to find the first data field on an object that implements two interfaces: IInterface and the “anonymous method interface” for this closure. This explains why an anonymous method can’t capture by-reference parameters
Os aconsejo su lectura (aunque está en ingles), pero creo que se puede entender ( y desde luego si yo lo entiendo vosotros casi con seguridad también, porque mi inglés todavía anda verde como el brote del olivo)
Al mismo tiempo que leía los fragmentos de código, algunas neuronas elucubraban hipótesis que me sugería la misma contemplación de aquellas lineas escritas. Y es que me preguntaba si era posible que un método anónimo invocara otro anidado, lo cual me sorprendía a mi mismo que tuviera utilidad alguna. Pero que lechess!!! Nadie puede estar preguntandose algo y esperar que le respondan, así que modifiqué con un par de lineas para adaptarlo a mi nueva duda existencial…
program Project1; {$APPTYPE CONSOLE} uses SysUtils, Dialogs; type TAnonProc = reference to procedure; procedure doStuff; var counter: integer; i: integer; countProc: TAnonProc; begin counter := 4; countProc := procedure begin countProc:= procedure begin countProc:= procedure begin inc(counter); end; end; end; for i := 1 to 10 do countProc; writeln(counter); end; begin try doStuff; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; readln; end.
Esta es la imagen que mostraba la ventana tras ejecutar el código. Logico ¿no? El valor del contador se inicia en cuatro y en el bucle se itera el codigo diez veces, de las cuales dos solo asignan el valor de la referencia al método.
¡Y yo que pensaba que iba a dar un error o algo similar!
Cuando era niño, y de eso ya hace algunos años, comercializaban unas bolsas de patatas fritas en las que se podía ver un niño, que sujetaba con alegria la bolsa. Si uno se fijaba con detalle, en la bolsa que el niño sujetaba, se podía ver la imagen del mismo niño sujetando las patatas, y así hasta donde se perdía nuestra imaginación y nuestra curiosidad…
🙂
en el bucle se itera el codigo diez veces, de las cuales dos solo asignan el valor de la referencia al método
Me tomó un minuto comprenderlo. Muy cierto Salvador, las dos primeras pasadas del ciclo solamente asignan un procedimiento a la variable CountProc. Es a partir de la pasada #3 cuando se ejecuta al procedimiento Inc. 🙂
Muy interesante.
Al González. 🙂
Me gustaMe gusta