Schreiben Sie Ihren eigenen Destruktor (alias Finalizer) in 99,99% aller Fälle falsch. Sie sind erforderlich, um sicherzustellen, dass Ihre Klasse eine Betriebssystemressource freigibt, die nicht automatisch vom .NET-Framework verwaltet wird und nicht ordnungsgemäß vom Benutzer Ihrer Klasse freigegeben wurde.
Dies beginnt damit, dass Sie zuerst die Betriebssystemressource in Ihrem eigenen Code reservieren müssen. Das erfordert immer eine Art P/Invoke. Dies ist sehr selten erforderlich, war es die Aufgabe der .NET-Programmierer bei Microsoft arbeiten, um das zu kümmern.
Sie taten im Falle von StreamWriter. Durch mehrere Ebenen ist es ein Wrapper um ein Datei-Handle, das mit CreateFile() erstellt wurde. Die Klasse, die das Handle erstellt hat, ist auch diejenige, die für das Schreiben des Finalizers zuständig ist. Microsoft-Code, nicht deins.
Eine solche Klasse implementiert IDisposable immer, sodass der Benutzer der Klasse die Möglichkeit erhält, die Ressource zu veröffentlichen, wenn sie fertig ist, anstatt darauf zu warten, dass der Finalizer die Aufgabe erledigt. StreamWriter implementiert IDisposable.
Ihr StreamWriter-Objekt ist natürlich ein privates Implementierungsdetail Ihrer Klasse. Sie wissen nicht, wann der Benutzer mit Ihrer Logger-Klasse fertig ist. Sie können StreamWriter.Dispose() nicht automatisch aufrufen. Sie benötigen Hilfe.
Holen Sie sich diese Hilfe, indem Sie IDisposable selbst implementieren.Der Benutzer Ihrer Klasse kann Entsorgen jetzt anrufen oder die Anweisung using verwenden, wie sie mit einem der Framework-Klassen würden:
class Logger : IDisposable {
private StreamWriter sw;
public void Dispose() {
sw.Dispose(); // Or sw.Close(), same thing
}
// etc...
}
Nun, das ist, was man in 99,9% aller Fälle tun soll. Aber nicht hier. Auf die Gefahr hin, Sie fatal zu verwirren: Wenn Sie IDisposable implementieren, muss der Benutzer Ihrer Klasse auch eine angemessene Gelegenheit haben, die Dispose() -Methode aufzurufen. Das ist normalerweise kein großes Problem, außer für eine Logger-Typklasse. Es ist sehr wahrscheinlich, dass Ihr Benutzer sich bis zum letzten Moment anmelden möchte. So kann sie beispielsweise eine nicht behandelte Ausnahme von AppDomain.UnhandledException protokollieren.
Wann sollte Dispose() in diesem Fall aufgerufen werden? Sie könnten es tun, wenn Ihr Programm beendet wird. Aber das ist nicht der Punkt, es hat wenig Sinn, Ressourcen frühzeitig freizugeben, wenn es beim Beenden des Programms passiert.
Ein Logger hat die spezielle Anforderung, in allen Fällen ordnungsgemäß herunterzufahren. Dazu müssen Sie die StreamWriter.AutoFlush-Eigenschaft auf true festlegen, damit die Protokollierungsausgabe sofort nach dem Schreiben gelöscht wird. Angesichts der Schwierigkeit, Dispose() ordnungsgemäß aufzurufen, ist es jetzt besser, IDisposable nicht zu implementieren und Microsoft Finalizer das Dateihandle zu schließen.
Fwiw, es gibt eine andere Klasse im Framework, die das gleiche Problem hat. Die Thread-Klasse verwendet vier Betriebssystemhandles, implementiert jedoch IDisposable nicht. Der Aufruf von Thread.Dispose() ist wirklich peinlich, so Microsoft hat es nicht implementiert. Dies führt zu Problemen, wenn Sie ein Programm schreiben, das viele Threads erstellt, aber nie den neuen Operator zum Erstellen von Klassenobjekten verwendet. Es wurde getan.
Last but not least: Schreiben eines Loggers ist ziemlich schwierig, wie oben erläutert. Log4net ist eine beliebte Lösung. NLog ist eine bessere Mausefalle, eine Bibliothek, die dotnetty fühlt und arbeitet, anstatt sich wie ein Java-Port zu fühlen.
Es gibt ein korrektes Muster zum Implementieren von Einwegobjekten mit Destruktoren. Das ist es nicht. Wie du herausgefunden hast, wenn du das Muster falsch findest, bekommst du die Freude, Fehler im Finalizer-Thread zu debuggen. Informiere dich über das Muster und setze es korrekt um. http://msdn.microsoft.com/en-us/magazine/cc163392.aspx –