2008-10-24 6 views
279
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?Acceso al cierre modificado

(este tema continuó here)

+1

Tener un blog que explica el acceso al tema de cierre de modificación: http://www.jarloo.com/access-to-modified-closure/ – Kelly

+6

Enlace está fuera, pero me pareció que en WebArchive: https: //web.archive.org/web/20150326104221/http://www.jarloo.com/access-to-modified-closure/ –

Respuesta

284

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

Si estabas salvando el delegado y su uso posterior, sin embargo, que iba a encontrar que todos los delegados lanzar excepciones cuando se trata de acceder a los archivos [i] - son la captura de la variable de i en lugar de su valor en el momento de la creación de delegados.

En resumen, es algo que debe tenerse en cuenta como una trampa potencial, pero en este caso no le hará daño.

Consulte el bottom of this page para obtener un ejemplo más complejo donde los resultados son contradictorios.

24

Sé que esta es una vieja pregunta, pero recientemente he estado estudiando cierres y pensé que una muestra de código podría ser útil. Detrás de escena, el compilador está generando una clase que representa un cierre léxico para su llamada de función. Es probable que se ve algo como:

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 es eficaz 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 se va a almacenar y luego invocar a los predicados, se podría ver que cada llamada a los predicados de verdad se llama el mismo método en el misma instancia de la clase de cierre y, por lo tanto, usaría el mismo valor para i.

2

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

las variables externas capturadas Cuando una variable exterior está referenciado por una función anónima, se dice que la variable exterior para haber sido capturado 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 con la 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 expresiones creados a partir de la función anónima sean elegibles para la recolección de elementos no utilizados.

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/expressions#outer-variables

Cuando una variable local o un parámetro valor es capturado por una función anónima, la variable local o parámetro ya no se considera como una variable fija (variables movibles fijo y), pero en cambio se considera que es una variable movible. Por lo tanto, cualquier código inseguro que tome la dirección de una variable externa capturada primero debe usar la instrucción fija para corregir la variable. Tenga en cuenta que, a diferencia de una variable no capturada, una variable local capturada puede exponerse simultáneamente a múltiples hilos de ejecución.

Cuestiones relacionadas