Pregunta

Encontré lo siguiente bastante extraño. Por otra parte, en su mayoría he usado cierres en lenguajes dinámicos que no deberían ser sospechosos para el mismo "error". Lo siguiente hace infeliz al compilador:

VoidFunction t = delegate { int i = 0; };

int i = 1;

Dice:

  

Una variable local llamada 'i' no puede ser   declarado en este ámbito porque   le daría un significado diferente a 'i',   que ya se usa en un 'niño'   alcance para denotar algo mas

Esto significa básicamente que las variables declaradas dentro de un delegado tendrán el alcance de la función declarada. No es exactamente lo que habría esperado. Ni siquiera he intentado llamar a la función. Al menos Common Lisp tiene una característica en la que dice que una variable debe tener un nombre dinámico, si realmente desea que sea local. Esto es particularmente importante cuando se crean macros que no se filtran, pero algo así también sería útil aquí.

¿Entonces me pregunto qué hacen otras personas para solucionar este problema?

Para aclarar, estoy buscando una solución donde las variables que declaro en el delegado no interfieran con las variables declaradas después de el delegado. Y todavía quiero poder capturar variables declaradas antes del delegado.

¿Fue útil?

Solución

Tiene que ser así para permitir que los métodos anónimos (y lambdas) utilicen variables locales y parámetros incluidos en el método que contiene.

Las soluciones alternativas son utilizar nombres diferentes para la variable o crear un método ordinario.

Otros consejos

El " cierre " creado por una función anónima es algo diferente de la creada en otros idiomas dinámicos (usaré Javascript como ejemplo).

function thing() {
    var o1 = {n:1}
    var o2 = {dummy:"Hello"}
    return function() { return o1.n++; }
}

var fn = thing();
alert(fn());
alert(fn());

Este pequeño fragmento de javascript mostrará 1 y luego 2. La función anónima puede acceder a la variable o1 porque existe en su cadena de alcance. Sin embargo, la función anónima tiene un alcance totalmente independiente en el que podría crear otra variable o1 y, por lo tanto, ocultar cualquier otra en la cadena de alcance. Tenga en cuenta también que todas las variables en la cadena completa permanecen, por lo tanto, o2 continuaría existiendo manteniendo una referencia de objeto mientras fn varialbe mantenga la referencia de la función.

Ahora compare con las funciones anónimas de C #: -

class C1 { public int n {get; set;} }
class C2 { public string dummy { get; set; } }

Func<int> thing() {
   var o1 = new C1() {n=1};
   var o2 = new C2() {dummy="Hello"};
   return delegate { return o1.n++; };
}
...
Func<int> fn = thing();
Console.WriteLine(fn());
Console.WriteLine(fn());

En este caso, la función anónima no crea un ámbito verdaderamente independiente más de lo que la declaración de variable en cualquier otro bloque de código en función {} (utilizado en un foreach , si , etc.)

Por lo tanto, se aplican las mismas reglas, el código fuera del bloque no puede acceder a las variables declaradas dentro del bloque, pero tampoco se puede reutilizar un identificador.

Se crea un cierre cuando la función anónima se pasa fuera de la función en la que se creó. La variación del ejemplo de Javascript es que solo permanecerán aquellas variables realmente utilizadas por la función anónima, por lo tanto, en este caso, el objeto mantenido. by o2 estará disponible para GC tan pronto como la cosa se complete,

También obtendrás CS0136 de un código como este:

  int i = 0;
  if (i == 0) {
    int i = 1;
  }

El alcance de la segunda declaración de " i " No es ambiguo, los lenguajes como C ++ no tienen ningún problema. Pero los diseñadores del lenguaje C # decidieron prohibirlo. Dado el fragmento anterior, ¿crees que todavía es una mala idea? Agrega un montón de código adicional y podrías mirar este código por un tiempo y no ver el error.

La solución es trivial e indolora, solo aparece con un nombre de variable diferente.

Es porque el delegado puede hacer referencia a variables fuera del delegado:

int i = 1;
VoidFunction t = delegate { Console.WriteLine(i); };

Si recuerdo correctamente, el compilador crea un miembro de clase de las variables externas a las que se hace referencia en el método anónimo, para que esto funcione.

Aquí hay una solución:

class Program
    {
        void Main()
        {
            VoidFunction t = RealFunction;
            int i = 1;
        }
        delegate void VoidFunction();
        void RealFunction() { int i = 0; }
    } 

En realidad, el error no parece tener nada que ver con delegados anónimos o expresiones lamda. Si intentas compilar el siguiente programa ...

using System;

class Program
{
    static void Main()
    {
        // Action t = delegate
        {
            int i = 0;
        };

        int i = 1;
    }
}

... obtienes exactamente el mismo error, no importa si comentas en la línea o no. La error help muestra un caso muy similar. Creo que es razonable rechazar ambos casos porque los programadores podrían confundir las dos variables.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top