UPDATE : Ich habe diese Frage als Grundlage für einen Artikel verwendet, der hier zu finden ist . Weitere Informationen zu diesem Thema finden Sie hier. Danke für die gute Frage!
Obwohl die Antwort von Schabse natürlich richtig ist und die gestellte Frage beantwortet, gibt es eine wichtige Variante Ihrer Frage, die Sie nicht gestellt haben:
Was passiert, wenn font4 = new Font()Würfe ausgeführt werden, nachdem die nicht verwaltete Ressource vom Konstruktor zugewiesen wurde, aber bevor der ctor zurückkehrt und font4die Referenz ausfüllt ?
Lassen Sie mich das etwas klarer machen. Angenommen, wir haben:
public sealed class Foo : IDisposable
{
private int handle = 0;
private bool disposed = false;
public Foo()
{
Blah1();
int x = AllocateResource();
Blah2();
this.handle = x;
Blah3();
}
~Foo()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
if (this.handle != 0)
DeallocateResource(this.handle);
this.handle = 0;
this.disposed = true;
}
}
}
Jetzt haben wir
using(Foo foo = new Foo())
Whatever(foo);
Dies ist das gleiche wie
{
Foo foo = new Foo();
try
{
Whatever(foo);
}
finally
{
IDisposable d = foo as IDisposable;
if (d != null)
d.Dispose();
}
}
OK. Angenommen, WhateverWürfe. Dann wird der finallyBlock ausgeführt und die Ressource freigegeben. Kein Problem.
Angenommen, Blah1()Würfe. Dann erfolgt der Wurf, bevor die Ressource zugewiesen wird. Das Objekt wurde zugewiesen, aber der ctor kehrt nie zurück, wird also foonie ausgefüllt. Wir haben das nie eingegeben, tryalso geben wir auch nie das ein finally. Die Objektreferenz wurde verwaist. Schließlich wird der GC dies feststellen und in die Finalizer-Warteschlange stellen. handleist immer noch Null, also macht der Finalizer nichts. Beachten Sie, dass der Finalizer gegenüber einem Objekt, das finalisiert wird und dessen Konstruktor nie abgeschlossen wurde, robust sein muss . Sie müssen so starke Finalizer schreiben. Dies ist ein weiterer Grund, warum Sie das Schreiben von Finalisierern Experten überlassen und nicht versuchen sollten, dies selbst zu tun.
Angenommen, Blah3()Würfe. Der Wurf erfolgt nach der Zuweisung der Ressource. Aber auch hier foowird nie ausgefüllt, wir geben das nie ein finallyund das Objekt wird vom Finalizer-Thread bereinigt. Diesmal ist der Griff ungleich Null und der Finalizer bereinigt ihn. Wieder läuft der Finalizer auf einem Objekt, dessen Konstruktor nie erfolgreich war, aber der Finalizer wird trotzdem ausgeführt. Offensichtlich muss es, denn diesmal hatte es Arbeit zu erledigen.
Nehmen wir nun an, Blah2()wirft. Der Wurf erfolgt, nachdem die Ressource zugewiesen, aber vorher handle ausgefüllt wurde! Wieder läuft der Finalizer, aber jetzt handleist er immer noch Null und wir lecken den Griff!
Sie müssen äußerst cleveren Code schreiben , um dieses Leck zu verhindern. FontWen interessiert zum Teufel Ihre Ressource? Wir lecken einen Font-Handle, große Sache. Wenn Sie jedoch unbedingt verlangen, dass jede nicht verwaltete Ressource bereinigt wird, unabhängig vom Zeitpunkt der Ausnahmen , haben Sie ein sehr schwieriges Problem.
Die CLR muss dieses Problem mit Sperren lösen. Seit C # 4 wurden Sperren, die die lockAnweisung verwenden, folgendermaßen implementiert:
bool lockEntered = false;
object lockObject = whatever;
try
{
Monitor.Enter(lockObject, ref lockEntered);
lock body here
}
finally
{
if (lockEntered) Monitor.Exit(lockObject);
}
Enterwurde sehr sorgfältig geschrieben, so dass unabhängig davon, welche Ausnahmen ausgelöst werden , lockEnteredgenau dann auf true gesetzt wird, wenn die Sperre tatsächlich aufgehoben wurde. Wenn Sie ähnliche Anforderungen haben, müssen Sie tatsächlich schreiben:
public Foo()
{
Blah1();
AllocateResource(ref handle);
Blah2();
Blah3();
}
und schreibe AllocateResourcegeschickt Monitor.Enterso, dass unabhängig davon, was im Inneren passiert AllocateResource, das genau dannhandle ausgefüllt wird, wenn es freigegeben werden muss.
Die Beschreibung der Techniken hierfür würde den Rahmen dieser Antwort sprengen. Wenden Sie sich an einen Experten, wenn Sie diese Anforderung haben.