Accesso alla chiusura modificata


316
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 lamenta che si tratta di "accesso alla chiusura modificata". Qualcuno può far luce su questo?

(questo argomento è continuato qui )


6
Il link è uscito, ma l'ho trovato su WebArchive: web.archive.org/web/20150326104221/http://www.jarloo.com/…
Eric Wu

Risposte:


314

In questo caso, va bene, dato che in realtà stai eseguendo il delegato all'interno del ciclo.

Se salvassi il delegato e lo utilizzassi in seguito, tuttavia, scopriresti che tutti i delegati genererebbero eccezioni quando provano ad accedere ai file [i] - stanno acquisendo la variabile i anziché il suo valore al momento dei delegati creazione.

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

Vedi il fondo di questa pagina per un esempio più complesso in cui i risultati sono controintuitivi.


29

So che questa è una vecchia domanda, ma recentemente ho studiato le chiusure e ho pensato che un esempio di codice potesse essere utile. Dietro le quinte, il compilatore sta generando una classe che rappresenta una chiusura lessicale per la chiamata della funzione. Probabilmente sembra qualcosa del genere:

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 funzione funziona perché i predicati vengono richiamati immediatamente dopo la creazione. Il compilatore genererà qualcosa del tipo:

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 si dovesse archiviare e successivamente invocare i predicati, si vedrebbe che ogni singola chiamata ai predicati chiamerebbe davvero lo stesso metodo sulla stessa istanza della classe di chiusura e quindi utilizzerebbe lo stesso valore per io.


4

"files" è una variabile esterna acquisita perché è stata acquisita dalla funzione di delegato anonimo. La sua durata è estesa dalla funzione di delegato anonimo.

Variabili esterne catturate Quando una variabile esterna fa riferimento a una funzione anonima, si dice che la variabile esterna sia stata catturata dalla funzione anonima. Di solito, 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 acquisita viene estesa almeno fino a quando il delegato o l'albero delle espressioni creato dalla funzione anonima diventa idoneo per la garbage collection.

Variabili esterne su MSDN

Quando una variabile locale o un parametro valore viene acquisito da una funzione anonima, la variabile locale o il parametro non viene più considerata una variabile fissa (variabili fisse e mobili), ma viene invece considerata una variabile mobile. Pertanto, qualsiasi codice non sicuro che utilizza l'indirizzo di una variabile esterna acquisita deve innanzitutto utilizzare l'istruzione fixed per correggere la variabile. Si noti che, diversamente da una variabile non acquisita, una variabile locale acquisita può essere contemporaneamente esposta a più thread di esecuzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.