anonyme Methoden und Objektlebensdauer
-
Guten Tag,
ich bin gerade C# am lernen und bin nun an einer Stelle die mir ein wenig seltsam anmutet.
Ich nutze das Buch zu VisualC# 2010, welches bei Galileo Openbooks zu finden ist.
Im Kapitel für Delegates habe ich nun zu den anonymen Methoden gelesen,
dass solch eine Methode auch auf Elemente aus dem Übergeordneten Sichtbarkeitsbereich zugreifen könne.So weit so gut, der Rumpf einer anonymen Methode befindet sich ja immerhin
innerhalb der Methode in der diese erstellt wird.Doch gibt es jetzt das Problem, das Werttypen, welche ja auf dem Stack
angelegt werden, dass Ende einer Methode nicht überleben.
Das heißt, nutzt die anonyme Methode die ich über ein Delegate zurückgebe
Elemente aus dem Übergeordneten Sichtbarkeitsbereich der Methode in der sie
erstellt wurde, dürften diese Objekte ja gar nicht mehr verfügbar sein.meth() { int var = 5; myDel = delegate() { return var; } return myDel; }
Ungläublig habe ich das auch sofort getestet und es hat alles fehlerfrei
funktioniert.Eigentlich kein Problem, doch da ich im Bereich C++ öfters schon Dinge hatte
die per Definition nicht funktionieren dürften, auf Grund irgendwelcher
speziellen Umstände oder dem Debugbuild dennoch das erwartete Ergebnis lieferten
würde ich nun gerne gewissheit haben wie es um diese Sache steht.Kopieren anonyme Methoden extern verwendete Varialben nun also in irgendeinen speiellen Speicherbereich oder ist es nur ein Zufall das das
bei mir nun funktioniert? Vielleicht hat ja wer genauere Informationen
wie das funktioniert.das wars erstmal
cu
-
Referenztypen (
class
): Es wird immer nur eine Referenz auf das Objekt kopiert. Objekte werden erst dann vom garbage collector aufgeräumt, wenn keine erreichbaren Referenzen mehr auf sie existieren. So lange das noch der Fall ist bleiben die Objekte "am Leben", egal ob irgendwo der Scope einer Variablen verlassen wurde.
**
Werttypen (struct
):** Werden immer kopiert, außer es wird explizit angefordert, dass das nicht passieren soll.In deinem konkreten Beispiel wird also der Wert 5 in die Variable "myDel" kopiert, da es sich bei
int
um einen Werttyp handelt. Ein Kopieren auf den Stack findet meines Wissens nach nicht statt, da die anonyme Methode ohne Parameter aufgerufen wurde.
-
Vielleicht habe ich dich falsch verstanden, doch ich übergebe hier doch nichts.
5 Steht nicht in myDel. MyDel ist ein Delegate.
Ein Delegate speichert die Adresse zu einer Methode.
Die Methode die durch myDel referenziert wird, bedient sich einer lokalen
Variable aus dem Stackframe von "meth()" (werttyp, liegt auf dem Stack von meth)myDel wird zurückgegeben und irgendwann aufgerufen nachdem der Stackframe zu meth bereits abgebaut wurde.
Das heißt, der Rumpf den ich in meth als anonyme methode definiert habe,
wird ausgeführt nachdem der stackframe zu meth nicht mehr existiert (??).
Könnte ja genausogut zusätzlich ein Parameter nutzen, käme das gleiche bei raus.meth() // <-- irgendeine methode eben { int lokale_meth_varaible = 5; myDelegate = delegate(int par) { return par + lokale_meth_variable; } return myDelegate; } [...] myDelegateV2 = obj.meth(); // stackframe von meth abgebaut, "lokale_meth_variable" zerstört int test = myDelegateV2(5); // erg=10;
Ich stelle mir jetzt die Frage wie das gehandhabt wird.
-
Der Speicher in C# wird vom Garbage Collector verwaltet. Dies erlaubt zahlreiche zusätzliche Möglichkeiten. So ist es möglich, dass eine Variable nicht sofort zerstört wird, sondern konserviert werden kann. Ihre Lebensdauer wird somit an ein anderes Objekt gebunden, in diesem Fall die Delegate Variable. Die Stack-Variable wird nicht mal kopiert, wie dies O.o gemeint hat. Sie wird wirklich erhalten. Wobei ich mir vorstellen könnte, dass dies durch Boxing geschieht, wodurch trotzdem eine Kopie stattfindet. Aber jedes Delegate, welches in der Methode erstellt wird, hat eine Referenz auf dasselbe Objekt. Siehe dir dazu dieses Beispiel an:
using System; namespace ShartTest { class Program { static Action[] Test() { int x = 0; Action a1 = delegate() { ++x; Console.WriteLine(x); }; Action a2 = delegate() { --x; Console.WriteLine(x); }; return new Action[] { a1, a2 }; } static void Main(string[] args) { Action[] actions = Test(); actions[0](); // 1 actions[0](); // 2 actions[1](); // 1 actions[0](); // 2 actions[1](); // 1 actions[1](); // 0 actions[0](); // 1 Console.ReadKey(true); } } }
Undefiniertes Verhalten gibt es nicht in C#. Das ist also alles richtig und definiert. Siehe auch hier (unter Remarks):
http://msdn.microsoft.com/en-us/library/0yw3tz5k.aspxSowas nennt man grundsätzlich auch Closure. Wobei die Begriffe zum Teil etwas variieren.
Grüssli
-
Vielen Dank für die hilfreichen Information und die Links,
ich denke sie haben mich dem Verständnis näher gebracht.Und noch ein Extra Danke dafür, dass du dir die Mühe gemacht hast für mich ein Codebeispiel zusammenzustellen.
cu
-
Wenn man deine Frage genau betrachtet, dann ist dein Problem nicht der Delegate an sich, sondern die anonyme Methode in der die Variable aus dem übergeordneten Scope verwendet wird.
Anonyme Methoden die auf Variablen im äußeren Scope zugreifen werden vom Compiler in kleine Klassen übersetzt die als Membervariablen die Variablen des äußeren Scopes haben.
Genauer kannst du es hier nachlesen: http://msdn.microsoft.com/en-us/magazine/cc163970.aspx#S7