Wie erkennt man, ob eine IDisposable-Objektreferenz vorhanden ist?

84

Gibt es eine Methode oder eine andere leichte Methode, um zu überprüfen, ob sich eine Referenz auf ein entsorgtes Objekt bezieht?

PS - Dies ist nur eine Kuriosität (gut schlafen, nicht im Produktionscode). Ja, ich weiß, dass ich das ObjectDisposedExceptionbeim Versuch, auf ein Mitglied des Objekts zuzugreifen, abfangen kann .

Neil C. Obremski
quelle
10
Keine Ahnung. Es scheint merkwürdig, dass es keine bool IsDisposed { get; }Erklärung dazu gibt System.IDisposable.
nicodemus13
2
@ nicodemus13: Die DisposeMethode weist ein Objekt an, alle Ressourcen freizugeben, die es erworben, aber noch nicht freigegeben hat. Wenn ein Objekt niemals Ressourcen enthält, muss seine DisposeMethode im Allgemeinen nichts tun. Wenn der Typ deklariert void IDisposable.Dispose() {};, kann er andernfalls IDisposableohne Overhead pro Instanz ignorieren . Eine IsDisposedEigenschaft, von der erwartet wurde, dass sie nach einem DisposeAufruf wahr wird , würde das Hinzufügen eines ansonsten unnötigen Booleschen Flags zu jeder Instanz vieler Typen erfordern, die sonst ignoriert werden könnten Dispose.
Supercat
1
Aber wo immer Sie eine Methode für ein Objekt aufrufen, das implementiert wird IDisposable, wie können Sie überprüfen, ob es zuerst entsorgt wurde? Anstatt davon auszugehen, dass dies nicht der Fall ist, und eine Ausnahme zu erwischen? Oder sollen Sie das Leben so gestalten, dass Sie immer wissen, ob es entsorgt ist oder nicht?
nicodemus13
3
@ nicodemus13: Man sollte ein Objekt im Allgemeinen nicht verwenden, ohne zu wissen, dass es nicht entsorgt wurde und wird, außer in Fällen, in denen man bereit ist, die Entsorgung des Objekts durch externen Code als Signal zu betrachten, um anstehende Aktionen damit abzubrechen . Ein IsDisposedFlag kann dazu beitragen, zu verhindern, dass Code Zeit für Vorgänge verschwendet, die möglicherweise nicht erfolgreich sind. In dem Fall, in dem ein Objekt zwischen der IsDisposedPrüfung und dem Versuch, es zu verwenden, entsorgt wird, müssen jedoch noch Ausnahmen behandelt werden .
Supercat
WeakReferencescheint hier relevant. Es ist nicht gerade ein IDipose-Detektor, aber es sagt Ihnen, ob es GC-Detektor ist
Malachi

Antworten:

46

Nein - Die Standardimplementierung des IDisposable-Musters unterstützt dies nicht

Dandikas
quelle
40

System.Windows.Forms.Controlhat eine IsDisposedEigenschaft, die nach dem Dispose()Aufruf auf true gesetzt ist . In Ihren eigenen IDisposable-Objekten können Sie problemlos eine ähnliche Eigenschaft erstellen.

Ryan Lundy
quelle
Das OP suchte nach einer ähnlichen Eigenschaft für Objekte, die er nicht erstellt. Dies wäre eine gute Idee für Objekte, die wir erstellen, aber die meisten verfügbaren Klassen in .NET folgen nicht dieser Konvention. Dandikas Antwort ist richtig.
Krillgar
2
@krillgar, es gibt nichts in der Frage des OP, was Ihre Behauptung stützt.
Ryan Lundy
18

Es ist nichts eingebaut, was dies zulässt. Sie müssten eine boolesche IsDisposed-Eigenschaft verfügbar machen, die ein internes disposed-Flag widerspiegelt.

public class SimpleCleanup : IDisposable
{
    private bool disposed = false;

    public bool IsDisposed
    {
       get
       {
          return disposed;
       }
    }

    public SimpleCleanup()
    {
        this.handle = /*...*/;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
               // free only managed resources here
            }

            // free unmanaged resources here
            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
    }
}
Scott Dorman
quelle
Übrigens, wenn man dieses Muster verwendet, hilft es, eine neue Schnittstelle ( IDisposablePlusoder was auch immer) zu definieren , die von IDisposableund enthält bool IsDisposed { get; }. Auf diese Weise können Sie leicht erkennen, welche Ihrer IDisposableObjekte unterstützt werden IsDisposed.
ToolmakerSteve
Ich glaube nicht, dass Sie eine Schnittstelle erben können, weil C # funktioniert. Platzieren einer Schnittstelle, nachdem ein Doppelpunkt sie geerbt hat. Ich gehe davon aus, dass es die Schnittstelle auf der anderen Seite implementieren würde.
Moses
9

Wenn es nicht Ihre Klasse ist und keine IsDisposed-Eigenschaft (oder etwas Ähnliches - der Name ist nur eine Konvention) bereitstellt, haben Sie keine Möglichkeit zu wissen.

Wenn es sich jedoch um Ihre Klasse handelt und Sie der kanonischen IDisposable-Implementierung folgen , legen Sie einfach das Feld _disposed oder _isDisposed als Eigenschaft offen und überprüfen Sie dies.

jop
quelle
2

Die DisposeMethode ist erforderlich, um alle erforderlichen Bereinigungen durchzuführen, bevor ein Objekt verlassen wird. Wenn keine Bereinigung erforderlich ist, muss nichts unternommen werden. Wenn ein Objekt nachverfolgen muss, ob es entsorgt wurde, selbst wenn die DisposeMethode sonst nichts tun würde, müssten viele IDisposableObjekte ein Flag hinzufügen, um einen sehr begrenzten Nutzen zu erzielen.

Es wäre möglicherweise hilfreich gewesen, wenn IDisposablezwei Eigenschaften enthalten wären - eine, die angibt, ob ein Objekt entsorgt werden muss , und eine, die angibt, dass das Objekt durch Entsorgung nicht unbrauchbar gemacht wurde. Bei Objekten, bei denen die Entsorgung tatsächlich etwas bewirkt, sind beide Werte zunächst wahr und werden danach falsch Dispose. Bei Objekten, bei denen für die Entsorgung keine Bereinigung erforderlich ist, kann die erste Methode immer false und die zweite immer true zurückgeben, ohne dass irgendwo ein Flag gespeichert werden muss. Ich glaube jedoch nicht, dass diese jetzt zu .NET hinzugefügt werden können.

Superkatze
quelle
IMHO, zwei Flaggen ist übertrieben. Ich denke, es ist besser, sich an das übliche Paradigma zu halten, bei dem man eine einzelne Flagge hat, sobald Dispose für ein Objekt aufgerufen wurde. Andernfalls fügen Sie Komplexität hinzu, nur um zu wissen, dass bestimmte Objekte "immer noch nützlich" sind, obwohl Dispose für sie aufgerufen wurde. Es lohnt sich nicht, diesen Weg zu gehen.
ToolmakerSteve
@ToolmakerSteve: Es würde im Allgemeinen null oder eins Flags geben. Für Objekte, die entsorgt werden müssen, würden die Eigenschaften "muss entsorgt werden" und "ist nützlich" vor der Entsorgung "wahr / wahr" und danach "falsch / falsch" ergeben, aber für Objekte, bei denen die Entsorgung ein No-Op wäre, würden beide bedingungslos "false / true" zurückgeben. Zu sagen, dass ein Objekt immer noch entsorgt werden muss, wenn dies niemals der Fall ist, oder dass ein Objekt nicht nützlich ist, wenn es immer vorhanden ist, wäre ziemlich schwierig. Ich nehme an, ein anderer Ansatz wäre die Verwendung eines Aufzählungstyps, um anzugeben, ob ein Typ entsorgt werden muss, entsorgt wurde oder sich einfach nicht darum kümmert.
Supercat
@ToolmakerSteve: Ich denke, der große Grund, warum IDisposablees keine DisposedEigenschaft gibt, ist, dass es als seltsam empfunden worden wäre, Objekte zu haben, bei denen der Aufruf Disposeeine solche Eigenschaft nicht festlegen würde true, aber dass Objekte nachverfolgen müssen, ob Disposesie in Fällen aufgerufen wurden, in denen Sie hätten sonst keinen Grund zur Sorge, würden erhebliche Kosten und wenig Nutzen verursachen.
Supercat
1

Ich sehe, dass dies alt ist, aber ich habe keine Antwort gesehen. Einige nicht alle verfügbaren Objekte wie ein DataSet verfügen über ein entsorgtes Ereignis, das Sie anhängen können.

class DisposeSample : IDisposable
{
    DataSet myDataSet = new DataSet();
    private bool _isDisposed;

    public DisposeSample()
    {
        // attach dispose event for myDataSet
        myDataSet.Disposed += MyDataSet_Disposed;
    }

    private void MyDataSet_Disposed(object sender, EventArgs e)
    {
        //Event triggers when myDataSet is disposed
        _isDisposed = true; // set private bool variable as true 
    }


    public void Dispose()
    {
        if (!_isDisposed) // only dispose if has not been disposed;
            myDataSet?.Dispose(); // only dispose if myDataSet is not null;
    }
}
Moses
quelle
Gut zu wissen. Insbesondere ist Disposedevent ein Mitglied der System.ComponentModel.IComponentSchnittstelle.
ToolmakerSteve
-1

Ich deklariere die Objekte gerne, ohne sie zu initialisieren, aber setze ihre Standardwerte auf Nothing. Dann schreibe ich am Ende der Schleife:

If anObject IsNot Nothing Then anObject.Dispose()

Hier ist ein vollständiges Beispiel:

Public Sub Example()
    Dim inputPdf As PdfReader = Nothing, inputDoc As Document = Nothing, outputWriter As PdfWriter = Nothing

    'code goes here that may or may not end up using all three objects, 
    ' such as when I see that there aren't enough pages in the pdf once I open  
    ' the pdfreader and then abort by jumping to my cleanup routine using a goto ..

GoodExit:
    If inputPdf IsNot Nothing Then inputPdf.Dispose()
    If inputDoc IsNot Nothing Then inputDoc.Dispose()
    If outputWriter IsNot Nothing Then outputWriter.Dispose()
End Sub

Dies funktioniert auch hervorragend, um Ihre Hauptobjekte an die Spitze einer Routine zu setzen, sie in einer TryRoutine zu verwenden und sie dann in einem FinallyBlock zu entsorgen :

Private Sub Test()
    Dim aForm As System.Windows.Forms.Form = Nothing
    Try
        Dim sName As String = aForm.Name  'null ref should occur
    Catch ex As Exception
        'got null exception, no doubt
    Finally
        'proper disposal occurs, error or no error, initialized or not..
        If aForm IsNot Nothing Then aForm.Dispose()
    End Try
End Sub
JeffreyDurham
quelle
6
@ LarsHöppner: Das Wesentliche der Frage ist sprachunabhängig, und gute C # -Entwickler sollten wahrscheinlich mindestens genug VB.NET kennen, um den obigen Code zu lesen (und VB.NET-Entwickler sollten ebenfalls genug C # lernen, um C # -Code zu lesen, der dies nicht tut etwas besonders Exotisches tun).
Supercat
3
Warum sollten Sie all dies tun, anstatt eine UsingAnweisung zu verwenden? Das gab es sicherlich schon 2013, als diese Antwort geschrieben wurde.
Cody Gray
Wirklich "GoodExit:" Was ist das 1983 für ein GOTO? Bitte hör auf damit.
Moses
Dies beantwortet die Frage nicht. Insbesondere wenn inputPdfIhre Antwort einmal auf einen anderen Wert (als Nichts) gesetzt wurde, kann Ihre Antwort nicht erkennen, ob inputPdfsie entsorgt wurde. Sie können dies teilweise beheben, indem Sie es inputPdf = Nothingnach der Entsorgung einstellen . Dies würde jedoch keinen anderen Variablen helfen , die auf dasselbe Objekt wie gezeigt wurden inputPdf. Das heißt , wenn Sie tun: inputPdf = New PdfReader, Dim pdf2 As PdfReader = inputPdf, inputPdf.Dispose, inputPdf = Nothing, gibt es noch keine Möglichkeit wäre , zu wissen , dass pdf2angeordnet ist (es ist das gleiche Objekt wie inputPdf).
ToolmakerSteve