2008-09-29 20 views
22

Diese ist eindeutig nicht scheint, als wäre es keine Best Practice. Kann jemand erklären, warum es keine Best Practice wäre oder wie das funktioniert? Bücher oder Artikel, die eine Erklärung liefern, würden geschätzt.Lokale Variablen mit Delegaten

//The constructor 
public Page_Index() { 

    //create a local value 
    string currentValue = "This is the FIRST value"; 

    //use the local variable in a delegate that fires later 
    this.Load += delegate(object sender, EventArgs e) { 
     Response.Write(currentValue); 
    }; 

    //change it again 
    currentValue = "This is the MODIFIED value"; 

} 

Der Wert, der ausgegeben wird, ist der zweite Wert "Modified". Welcher Teil der Compiler-Magie funktioniert? Ist das so einfach wie den Wert auf dem Heap zu verfolgen und später wieder abzurufen?

[Bearbeiten]: einige der Kommentare gegeben, einige der ursprünglichen Satz zu ändern ...

+0

Es ist nichts falsch mit dieser Praxis. Es ist nur fortgeschrittener als Anfänger es verstehen würden. – leppie

+0

abgeordnet; in der Tat kann es für sehr sauberes/elegantes Design sorgen - aber Sie müssen die Implikationen verstehen. –

+0

Das ist wirklich sehr interessant. Ich würde nicht denken, dass es eine gute Übung wäre, mit lokalen Variablen im Rahmen des zugewiesenen Delegierten zu spielen, aber Sie lernen ständig etwas Neues. – Hugoware

Antwort

27

current ist nicht mehr eine lokale Variable: Es ist ein erfasst variabel. Dies kompiliert, um so etwas wie:

class Foo { 
    public string currentValue; // yes, it is a field 

    public void SomeMethod(object sender, EventArgs e) { 
    Response.Write(currentValue); 
    } 
} 
... 
public Page_Index() { 
    Foo foo = new Foo(); 
    foo.currentValue = "This is the FIRST value"; 
    this.Load += foo.SomeMethod; 

    foo.currentValue = "This is the MODIFIED value"; 
} 

Jon Skeet hat eine wirklich gute aufzuschreiben dies in C# in Depth und einem separaten (nicht so detailliert) Diskussion here.

Beachten Sie, dass die Variable currentValue jetzt auf dem Heap ist, nicht auf dem Stack - dies hat viele Auswirkungen, nicht zuletzt, dass es jetzt von verschiedenen Aufrufern verwendet werden kann.

Dies ist anders als Java: In Java wird der Wert einer Variablen erfasst. In C# wird die Variable selbst erfasst.

+0

Ich schreibe nicht nur in C# auf (danke für den Stecker!), Ich habe einen Artikel, der C# und Java-Verschlüsse vergleicht und erklärt, warum sie nett sind: http://csharpindepth.com/Articles/ Chapter5/Closures.aspx –

+0

Ah - als Sie diesen Kommentar geschrieben haben, habe ich das oben beschriebene bearbeitet. Verdopple den Wert ;-p –

+0

@Mark: Könntest du "variable * value *" durch "* value * der Variable" in deinem letzten Satz austauschen, um deine großartige Antwort etwas zu verbessern? Da die Variable "currentValue" genannt wird, kann sie den Leser (wie mich) verwirren, indem sie zunächst denkt, dass der kursive "* value *" sich auf den * name * der Variablen bezieht ("welche Variable? Die mit * value *") – chiccodoro

0

Sie müssen den Wert der Variablen innerhalb der Schließung/Delegate erfassen, sonst kann es geändert werden, wie Sie gesehen haben.

Weisen Sie currentValue einer Variablen local (inside) für den Delegaten zu.

2

Ich nehme mehr die Frage, die ich gefragt habe ist, dass, wie es mit einer lokalen Variablen arbeitet [MG edit: „Ack - ignorieren diese ...“ wurde später hinzugefügt]

Das heißt die Stelle; es wirklich ist nicht eine lokale Variable mehr - zumindest nicht in Bezug auf wie wir normalerweise von ihnen denken (auf dem Stapel usw.). Es sieht wie eins aus, ist es aber nicht.

Und für Informationen, re "nicht gute Praxis" - anonyme Methoden und erfasste Variablen sind eigentlich ein unglaublich leistungsstarkes Werkzeug, vor allem bei der Arbeit mit Ereignissen. Fühlen Sie sich frei, sie zu benutzen, aber wenn Sie diesen Weg hinunter gehen, würde ich empfehlen, Jon's Buch aufzuheben, um sicherzustellen, dass Sie verstehen, was tatsächlich passiert.