c# resharper closures

c# - Acceso a cierre modificado



resharper closures (3)

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 esto es "acceso a un cierre modificado". ¿Alguien puede arrojar luz sobre esto?

(Este tema continuó here )


"archivos" es una variable externa capturada porque ha sido capturada por la función de delegado anónimo. Su duración se prolonga por la función de delegado anónimo.

Variables externas capturadas Cuando una variable anónima hace referencia a una variable externa, se dice que la variable anónima ha capturado la variable externa. 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 expresión creado a partir de la función anónima sea elegible para la recolección de basura.

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

Cuando una variable 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 primero usar la declaració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 varios subprocesos de ejecución.


En este caso, está bien, ya que en realidad está 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 lanzarán excepciones cuando intenten acceder a los archivos [i]: están capturando la variable i lugar de su valor en el momento de la Creación de delegados.

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

Vea la parte inferior de esta página para ver un ejemplo más complejo donde los resultados son contraintuitivos.


Sé que esta es una pregunta antigua, 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. Probablemente se parece a 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 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 realmente llamaría al mismo método en la misma instancia de la clase de cierre y, por lo tanto, usaría el mismo valor para yo.