2011-01-08 9 views
3

Gibt es eine Möglichkeit festzustellen, ob ein Objekt GC.SuppressFinalize aufgerufen hat oder nicht?Kann ich erkennen, ob ein Objekt GC.SuppressFinalize aufgerufen hat?

Ich habe ein Objekt, das so etwas wie dieses (für Klarheit elided ausgewachsene Dispose-Muster) aussieht:

public class ResourceWrapper { 
    private readonly bool _ownsResource; 
    private readonly UnmanagedResource _resource; 

    public ResourceWrapper(UnmanagedResource resource, bool ownsResource) { 
     _resource = resource; 
     _ownsResource = ownsResource; 
     if (!ownsResource) 
      GC.SuppressFinalize(this); 
    } 
    ~ResourceWrapper() { 
     if (_ownsResource) 
      // clean up the unmanaged resource 
    } 
} 

Wenn der ownsResource Konstruktorparameter false ist, dann ist die Finalizerthread nichts zu tun haben - so es scheint vernünftig (wenn ein bisschen schrullig) GC.SuppressFinalize direkt vom Konstruktor zu nennen. Da dieses Verhalten jedoch merkwürdig ist, bin ich sehr versucht, es in einem XML-Dokument-Kommentar zu notieren ... und wenn ich versucht bin, es zu kommentieren, dann sollte ich einen Komponententest dafür schreiben.

Aber während System.GC Methoden gesetzt hat ein finalizability des Objekts (SuppressFinalize, ReRegisterForFinalize), ich sehe keine Methoden erhalten ein finalizability des Objekts. Gibt es eine Möglichkeit, abzufragen, ob GC.SuppressFinalize für eine bestimmte Instanz aufgerufen wurde, kurz vor dem Kauf von Typemock oder dem Schreiben eines eigenen CLR-Hosts?

Antwort

3

Wenn Sie bestätigen möchten, dass die Finalisierung unterdrückt wurde, wenn Ihr Objekt die Ressource nicht besitzt, könnte der Finalizer möglicherweise bestätigen, dass er die Ressource besitzt? Der Test müsste GC.Collect und GC.WaitForPendingFinalizers ausführen, aber der Produktionscode hätte nichts Zusätzliches außer der Assert (die im Produktions-Build weggelassen werden könnte). Ein kleiner Vorbehalt mit der Assertion: Wenn der Thread, der das Objekt erstellt, zwischen dem Erstellen des Objekts und dem Festlegen des Besitzstatus stirbt, wird der Finalizer möglicherweise nicht ordnungsgemäß ausgeführt.

Nachdem ich das gesagt habe, frage ich mich, ob es besser wäre, einen abstrakten ResourceWrapper-Typ mit den getrennten Subtypen OwnedResourceWrapper und SharedResourceWrapper zu haben, der die betreffende Ressource besitzt oder nicht besitzt. Dann müsste der Subtyp, der keine Ressourcen besitzt, nicht unbedingt einen Finalizer haben. Beachten Sie, dass es für SharedResourceWrapper nützlich sein kann, IDisposable als No-Op zu implementieren.

+0

Beide gute Ideen, aber ich mag die zweite: Verschieben Sie die Verantwortung "Owner" auf ein separates Objekt. –

+0

Die Verwendung eines separaten Objekttyps ist IMHO geeignet, sauberer zu sein, wenn es funktioniert (d. H. Der Wrapper weiß, wann er erstellt wird, ob die Ressource im Besitz sein wird und dieser Status sich nie ändert), erfordert aber möglicherweise etwas mehr Code. Es ist sicherlich nützlich, beide Techniken zur Verfügung zu haben. Wie bereits in der obigen Anleitung erwähnt, werden die Finalizer standardmäßig ausgeführt, bevor sie eingerichtet werden. das scheint ein bisschen schlampig, aber wird wahrscheinlich in den meisten Szenarien kein Problem sein. – supercat

4

Dies ist nicht möglich, der GC liefert diese Informationen nicht. Ein guter Grund dafür ist, dass das Objekt nicht nur aus zwei Zuständen bestehen kann. Es befindet sich möglicherweise auch bereits in der Finalisierungswarteschlange oder wurde möglicherweise bereits finalisiert.

Ein benutzerdefinierter CLR-Host wird Ihnen damit nicht helfen, die Hosting-Schnittstelle bietet keine Haken in die GC. Sie können überprüfen, ob SuppressFinalize aufgerufen wurde, wenn dies einfach im Finalizer aktiviert werden sollte. Protokolliere es (schnell). Sie können das Gegenteil nicht beweisen.

Fwiw, die .NET-Frame-Klassen machen das nicht, sie lassen den Finalizer trotzdem laufen.

+0

Einige BCL-Klassen tun tatsächlich SuppressFinalize von ihren Konstruktoren, z. SqlConnection (obwohl ich keine gesehen habe, die es bedingt tun). –

2

Dies kann helfen (reductio absurdum). Ein Trick wäre ein Log (das könnte ein statischer Zustand sein) in den Finalizern und wenn jemand abwesend ist, hat er das Suppress Finalize aufgerufen, aber trotzdem kann man nicht sicher sein wann.

Dies funktioniert, wenn Sie der Autor des Typs sind.

+0

Ich dachte darüber nach. Das würde auch erfordern, GC.Collect und GC.WaitForPendingFinalizers von meinem Test zu benennen, obwohl das machbar ist. Aber ich hasse es, dass Produktionscode zusätzliche Arbeit leistet, die nur zum Testen dient. –

+0

Ich empfehle es überhaupt nicht, aber immer noch eine Lösung. Vielleicht finden Sie eine zuverlässigere Lösung von anderen. – Xaqron

Verwandte Themen