322 Stimmen

Zugang zum geänderten Abschluss

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
}

Die oben scheint zu funktionieren, obwohl ReSharper beschwert sich, dass dies "Zugriff auf modifizierte Schließung" ist. Kann jemand Licht auf diese vergießen?

(dieses Thema wird fortgesetzt ici )

8 Stimmen

Der Link ist nicht mehr verfügbar, aber ich habe ihn bei WebArchive gefunden: web.archive.org/web/20150326104221/http://www.jarloo.com/

319voto

Jon Skeet Punkte 1325502

In diesem Fall ist es in Ordnung, denn Sie führen den Delegaten tatsächlich aus innerhalb die Schleife.

Wenn Sie den Delegaten speichern und ihn später verwenden würden, würden Sie jedoch feststellen, dass alle Delegaten Ausnahmen auslösen würden, wenn sie versuchen, auf files[i] zuzugreifen - sie fangen die variabel i und nicht seinen Wert zum Zeitpunkt der Erstellung der Delegierten.

Kurz gesagt, es ist etwas, worauf man als potenziell Falle, aber in diesem Fall schadet es Ihnen nicht.

Siehe die unten auf dieser Seite für ein komplexeres Beispiel, bei dem die Ergebnisse kontraintuitiv sind.

31voto

gerrard00 Punkte 1497

Ich weiß, dass dies eine alte Frage ist, aber ich habe mich kürzlich mit Schließungen beschäftigt und dachte, ein Codebeispiel könnte nützlich sein. Hinter den Kulissen erzeugt der Compiler eine Klasse, die eine lexikalische Schließung für Ihren Funktionsaufruf darstellt. Wahrscheinlich sieht sie etwa so aus:

private sealed class Closure
{
    public string[] files;
    public int i;

    public bool YourAnonymousMethod(string name)
    {
        return name.Equals(this.files[this.i]);
    }
}

Wie oben erwähnt, funktioniert Ihre Funktion, weil die Prädikate unmittelbar nach der Erstellung aufgerufen werden. Der Compiler wird etwas wie erzeugen:

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

Wenn Sie hingegen die Prädikate speichern und später aufrufen würden, würden Sie sehen, dass jeder einzelne Aufruf der Prädikate in Wirklichkeit dieselbe Methode für dieselbe Instanz der Abschlussklasse aufruft und daher denselben Wert für i verwendet.

4voto

chris hu Punkte 418

"Dateien" ist ein erfasste äußere Variable weil sie von der anonymen Delegiertenfunktion erfasst wurde. Seine Lebensdauer wird durch die anonyme Delegiertenfunktion verlängert.

Erfasste äußere Variablen Wenn eine äußere Variable von einer anonymen Funktion referenziert wird, spricht man davon, dass die äußere Variable von der anonymen Funktion eingefangen wurde. Normalerweise ist die Lebensdauer einer lokalen Variable auf die Ausführung des Blocks oder der Anweisung beschränkt, mit der sie verbunden ist (Lokale Variablen). Die Lebensdauer einer gefangenen äußeren Variablen wird jedoch mindestens so lange verlängert, bis der von der anonymen Funktion erstellte Delegat oder Ausdrucksbaum für die Garbage Collection in Frage kommt.

Äußere Variablen auf MSDN

Wenn eine lokale Variable oder ein Wertparameter von einer anonymen Funktion erfasst wird, wird die lokale Variable oder der Parameter nicht mehr als feste Variable betrachtet (Feste und bewegliche Variablen), sondern als bewegliche Variable. Daher muss jeder unsichere Code, der die Adresse einer gefangenen äußeren Variablen übernimmt, zuerst die Anweisung fixed verwenden, um die Variable zu fixieren. Beachten Sie, dass eine gefangene lokale Variable im Gegensatz zu einer nicht gefangenen Variable gleichzeitig mehreren Threads der Ausführung ausgesetzt sein kann.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X