Accesso alla chiusura modificata

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 } 

Quanto sopra sembra funzionare bene anche se ReSharper si lamenta che si tratta di “accesso alla chiusura modificata”. Qualcuno può far luce su questo?

(questo argomento è continuato qui )

In questo caso, va bene, poiché in realtà stai eseguendo il delegato all’interno del ciclo.

Se stavi salvando il delegato e lo usassi in un secondo momento, tuttavia, scoprirai che tutti i delegati genererebbero eccezioni quando provano ad accedere ai file [i] – stanno catturando la variabile i piuttosto che il suo valore al momento della creazione di delegati.

In breve, è qualcosa di cui essere consapevole come una potenziale trappola, ma in questo caso non ti fa male.

Vedere la parte inferiore di questa pagina per un esempio più complesso in cui i risultati sono controintuitivi.

So che questa è una vecchia domanda, ma di recente ho studiato le chiusure e ho pensato che un esempio di codice potesse essere utile. Dietro le quinte, il compilatore sta generando una class che rappresenta una chiusura lessicale per la tua chiamata di funzione. Probabilmente somiglia a qualcosa:

 private sealed class Closure { public string[] files; public int i; public bool YourAnonymousMethod(string name) { return name.Equals(this.files[this.i]); } } 

Come accennato in precedenza, la tua funzione funziona perché i predicati vengono richiamati immediatamente dopo la creazione. Il compilatore genererà qualcosa come:

 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; } 

D'altra parte, se dovessi archiviare e successivamente invocare i predicati, vedresti che ogni singola chiamata ai predicati chiamerebbe lo stesso metodo sulla stessa istanza della class di chiusura e quindi userebbe lo stesso valore per io.

“file” è una variabile esterna catturata perché è stata acquisita dalla funzione di delegato anonimo. La sua durata è estesa dalla funzione di delegato anonimo.

Variabili esterne catturate Quando una variabile esterna è referenziata da una funzione anonima, si dice che la variabile esterna sia stata catturata dalla funzione anonima. Normalmente, la durata di una variabile locale è limitata all’esecuzione del blocco o dell’istruzione a cui è associata (variabili locali). Tuttavia, la durata di una variabile esterna catturata viene estesa almeno fino a quando il delegato o l’albero delle espressioni creati dalla funzione anonima non diventano idonei per la garbage collection.

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

Quando una variabile locale o un parametro di valore viene catturato da una funzione anonima, la variabile o il parametro locale non viene più considerato una variabile fissa (variabili fisse e mobili), ma viene invece considerato come una variabile mobile. Quindi qualsiasi codice non sicuro che prende l’indirizzo di una variabile esterna catturata deve prima usare l’istruzione fissa per correggere la variabile. Si noti che a differenza di una variabile non acquisita, una variabile locale catturata può essere esposta simultaneamente a più thread di esecuzione.