Ich verstehe, dass in .NET Finalizer ausgeführt werden, selbst wenn ein Objekt teilweise konstruiert ist (z. B. wenn eine Ausnahme aus seinem Konstruktor geworfen wird), aber was ist, wenn der Konstruktor überhaupt nicht ausgeführt wurde?
Hintergrund
Ich habe einige C ++ / CLI-Code, der effektiv das folgende tut (ich glaube nicht, dass dies C ++ / CLI-spezifisch ist, aber das ist die Situation, die ich zur Verfügung habe):
%Vor%Ich habe einen 100% wiederholbaren Fall, in dem, wenn eine Ausnahme aus FunctionThatReturnsAClassA () ausgelöst wird und dann ein GC ausgelöst wird (scheint durch diesen Code erneut ausgeführt zu werden, aber auch eine Weile funktioniert), ClassBs Finalizer wird aufgerufen.
Jetzt kann ich über Trace-Ausgabe bestätigen, dass der Konstruktor von ClassB nicht läuft (was natürlich zu erwarten ist). Irgendwie wurde objB anscheinend zugewiesen und der Finalizer-Liste hinzugefügt, bevor die Vorbedingungen für den Aufruf seines Konstruktors erfüllt waren (d. H. Das Ergebnis von FunctionThatReturnsAClassA () gesammelt wurde).
Dies geschieht nur in optimierten Release-Builds, die außerhalb des Debuggers ausgeführt werden. Es gibt eine Vielzahl von kleinen Änderungen, die ich machen kann, dass der Finalizer nicht ausgeführt wird - zum Beispiel Einfügen eines anderen Methodenaufrufs zwischen den beiden Anweisungen, oder (glaube ich, denke ich) Verschieben des "gcnew ClassB" in eine separate Funktion, die das zurückgibt Objekt.
Mir scheint, dass der Zuordnungsteil der gcnew-Anweisung irgendwie neu angeordnet und vor der vorherigen Anweisung ausgeführt wird, aber diese Neuordnung spiegelt sich nicht im generierten MSIL-Code wider (meine ursprüngliche Annahme, dass dies nur ein anderes C ++ / CLI war) Code gen Fehler). Außerdem zeigt der Vergleich des generierten MSIL-Codes zwischen dem Status "Buggy" und einem der Zustände "Fixed" keine unerwarteten strukturellen Änderungen.
Ich habe mir auch den generierten x86-Code im Debugger angeschaut und er sieht bisher nicht sonderlich komisch aus, aber ich habe ihn nicht so gründlich analysiert und ich kann dieses Verhalten im Debugger nicht reproduzieren Ich bin nicht 100% sicher, dass der Code, den ich vom Debugger bekomme, derselbe ist wie der Code, der das seltsame Verhalten zeigt.
Es könnte also eine MSIL- & gt; x86-Code-Gen-Eigenart sein oder es könnte eine Neuanordnung von Prozessoranweisungen sein (ersteres scheint wahrscheinlicher, aber ich habe nicht bestätigt, indem ich härter versuche, den genauen Code im Speicher zu bekommen, wenn das Verhalten auftritt - das ist mein nächster Schritt).
Frage
Also ist es (aus Mangel an einem besseren Begriff) gültig, dass die Zuweisung eines Objekts in .NET getrennt vom Konstruktoraufruf für dieses Objekt geschieden und neu angeordnet wird?
Wie in Kommentaren beschrieben, lautet die Antwort "Ja" - ein Finalizer kann ausgeführt werden, wenn ein Konstruktor nicht ausgeführt wurde oder nicht abgeschlossen wurde. Ein Finalizer kann jedoch nicht ausgeführt werden, wenn keine Zuweisung erfolgte (unabhängig vom Konstruktoraufruf).
Dies wird nun als JIT-Optimierungsfehler bestätigt: Ссылка