Acceso al Cierre Modificado
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í )
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.
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.
"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.