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 font4
die 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, Whatever
Würfe. Dann wird der finally
Block 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 foo
nie ausgefüllt. Wir haben das nie eingegeben, try
also geben wir auch nie das ein finally
. Die Objektreferenz wurde verwaist. Schließlich wird der GC dies feststellen und in die Finalizer-Warteschlange stellen. handle
ist 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 foo
wird nie ausgefüllt, wir geben das nie ein finally
und 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 handle
ist er immer noch Null und wir lecken den Griff!
Sie müssen äußerst cleveren Code schreiben , um dieses Leck zu verhindern. Font
Wen 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 lock
Anweisung 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);
}
Enter
wurde sehr sorgfältig geschrieben, so dass unabhängig davon, welche Ausnahmen ausgelöst werden , lockEntered
genau 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 AllocateResource
geschickt Monitor.Enter
so, 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.