c# - una - variables locales en java
Pensé que C#tiene un alcance léxico, pero ¿por qué este ejemplo muestra un comportamiento de alcance dinámico? (2)
var x = 1;
Func<int,int> f = y => x + y;
x = 2;
Console.WriteLine(f(1));
La salida es 3. Supongo que es 2, según http://www.cs.cornell.edu/~clarkson/courses/csci4223/2013sp/lec/lec12.pdf
Esta pregunta fue el tema de mi blog el 20 de mayo de 2013 . Gracias por la gran pregunta!
Estás malinterpretando lo que significa "ámbito léxico". Citemos el documento al que está vinculado:
el cuerpo de una función se evalúa en el entorno dinámico antiguo que existía en el momento en que se definió la función, no en el entorno actual cuando se llama a la función.
Aquí está su código:
int x = 1;
Func<int,int> f = y => x + y;
x = 2;
Console.WriteLine(f(1));
Ahora, ¿cuál es "el entorno dinámico que existe en el momento en que se definió la función"? Piense en un "entorno" como clase. Esa clase contiene un campo mutable para cada variable. Así que esto es lo mismo que:
Environment e = new Environment();
e.x = 1;
Func<int,int> f = y => e.x + y;
e.x = 2;
Console.WriteLine(f(1));
Cuando se evalúa f
, se busca x
en el entorno e que existía cuando se creó f . El contenido de ese entorno ha cambiado, pero el entorno al que está vinculado es el mismo entorno. (Tenga en cuenta que este es realmente el código que genera el compilador de C # . Cuando usa una variable local en un lambda, el compilador genera una clase especial de "entorno" y convierte cada uso del local en un uso de un campo.)
Permítame darle un ejemplo de cómo se vería el mundo si C # tuviera un alcance dinámico. Considera lo siguiente:
class P
{
static void M()
{
int x = 1;
Func<int, int> f = y => x + y;
x = 2;
N(f);
}
static void N(Func<int, int> g)
{
int x = 3;
Console.WriteLine(g(100));
}
}
Si C # tenía un alcance dinámico, esto se imprimiría "103" porque la evaluación de g
evalúa f
, y en un lenguaje de alcance dinámico, la evaluación de f
buscaría el valor de x
en el entorno actual . En el entorno actual , x
es 3. En el entorno que existía cuando se creó f
, x
es 2. De nuevo, el valor de x
en ese entorno ha cambiado; Como señala su documento, el medio ambiente es un entorno dinámico . Pero qué ambiente es relevante no cambia.
La mayoría de los idiomas en estos días no tienen un alcance dinámico, pero hay algunos. PostScript, por ejemplo, el lenguaje que se ejecuta en las impresoras, tiene un alcance dinámico.
Hay una sutileza con respecto al alcance léxico que el PDF no explica completamente. Su ejemplo realmente tiene dos variables diferentes llamadas x
, no reasigna el valor de la primera x
(y, de hecho, los lenguajes funcionales pueden no permitir la mutación).
C # tiene un ámbito léxico: busca x
en el punto de definición de la lambda, no cuando se invoca al delegado. Pero: x
resuelve en una variable, no en un valor, y lee el valor de la variable en el momento de la invocación.
Aquí hay un ejemplo más completo:
int InvokeIt( Func<int, int> f )
{
int x = 2;
return f(1);
}
Func<int, int> DefineIt()
{
int x = 1;
Func<int, int> d = (y => x + y);
x = 3; // <-- the PDF never does this
return d;
}
Console.WriteLine(InvokeIt(DefineIt()));
La lambda se enlaza a la variable x
que existe dentro de DefineIt
. El valor ( x = 1
) en el punto de definición es irrelevante. La variable se establece más tarde en x = 3
.
Pero claramente tampoco es un ámbito dinámico , porque no se usa x = 2
dentro de InvokeIt
.