Acceso al Cierre Modificado

Resuelto Vyas Bharghava asked hace 16 años • 3 respuestas
string [] files = new string[2];
files[0] = "ThinkFarAhead.Example.Settings.Configuration_Local.xml";
files[1] = "ThinkFarAhead.Example.Settings.Configuration_Global.xml";

//Resharper complains this is an "access to modified closure"
for (int i = 0; i < files.Length; i++ )
{
    // Resharper disable AccessToModifiedClosure
    if(Array.Exists(Assembly.GetExecutingAssembly().GetManifestResourceNames(),
    delegate(string name) { return name.Equals(files[i]); }))
         return Assembly.GetExecutingAssembly().GetManifestResourceStream(files[i]);
    // ReSharper restore AccessToModifiedClosure
}

Lo anterior parece funcionar bien, aunque ReSharper se queja de que se trata de "acceso al cierre modificado". ¿Alguien puede arrojar luz sobre esto?

(este tema continúa aquí )

Vyas Bharghava avatar Oct 25 '08 05:10 Vyas Bharghava
Aceptado

En este caso, está bien, ya que en realidad estás ejecutando el delegado dentro del bucle.

Sin embargo, si estuviera guardando el delegado y usándolo más tarde, encontraría que todos los delegados generarían excepciones al intentar acceder a los archivos [i]: están capturando la variable i en lugar de su valor en el momento de los delegados. creación.

En resumen, es algo a tener en cuenta como una trampa potencial , pero en este caso no te hace daño.

Consulte la parte inferior de esta página para ver un ejemplo más complejo donde los resultados son contrarios a la intuición.

Jon Skeet avatar Oct 24 '2008 22:10 Jon Skeet

Sé que esta es una vieja pregunta, pero recientemente estuve estudiando los cierres y pensé que un ejemplo de código podría ser útil. Detrás de escena, el compilador genera una clase que representa un cierre léxico para su llamada a función. Probablemente se parezca a:

private sealed class Closure
{
    public string[] files;
    public int i;

    public bool YourAnonymousMethod(string name)
    {
        return name.Equals(this.files[this.i]);
    }
}

Como se mencionó anteriormente, su función funciona porque los predicados se invocan inmediatamente después de la creación. El compilador generará algo como:

private string Works()
{
    var closure = new Closure();

    closure.files = new string[3];
    closure.files[0] = "notfoo";
    closure.files[1] = "bar";
    closure.files[2] = "notbaz";

    var arrayToSearch = new string[] { "foo", "bar", "baz" };

    //this works, because the predicates are being executed during the loop
    for (closure.i = 0; closure.i < closure.files.Length; closure.i++)
    {
        if (Array.Exists(arrayToSearch, closure.YourAnonymousMethod))
            return closure.files[closure.i];
    }

    return null;
}

Por otro lado, si almacenara y luego invocara los predicados, vería que cada llamada a los predicados en realidad estaría llamando al mismo método en la misma instancia de la clase de cierre y, por lo tanto, usaría el mismo valor para i.

gerrard00 avatar Mar 29 '2013 17:03 gerrard00

"archivos" es una variable externa capturada porque ha sido capturada por la función delegada anónima. Su vida útil se amplía mediante la función de delegado anónimo.

Variables externas capturadas Cuando una función anónima hace referencia a una variable externa, se dice que la variable externa ha sido capturada por la función anónima. Normalmente, la vida útil de una variable local se limita a la ejecución del bloque o declaración al que está asociada (variables locales). Sin embargo, la vida útil de una variable externa capturada se extiende al menos hasta que el delegado o el árbol de expresión creado a partir de la función anónima sea elegible para la recolección de basura.

Variables externas en MSDN

Cuando una función anónima captura una variable local o un parámetro de valor, la variable o parámetro local ya no se considera una variable fija (variables fijas y móviles), sino que se considera una variable móvil. Por lo tanto, cualquier código inseguro que tome la dirección de una variable externa capturada debe usar primero la declaración fija para arreglar la variable. Tenga en cuenta que, a diferencia de una variable no capturada, una variable local capturada puede exponerse simultáneamente a varios subprocesos de ejecución.

chris hu avatar Aug 13 '2017 23:08 chris hu