2010-04-29 29 views
10

Ich habe so viel C# -Code in meiner Zeit als Entwickler gesehen, der versucht, den GC zu helfen, indem er Variablen auf null setzt oder Dispose() auf Klassen (DataSet zum Beispiel) innerhalb ihrer eigenen Klassen aufruft Dispose() Methode, die Ich habe mich gefragt, ob es notwendig ist, es in einer verwalteten Umgebung zu implementieren.Wie man IDisposable richtig implementiert

Ist dieser Code eine Verschwendung von Zeit in seinem Designmuster?

class MyClass : IDisposable 
{ 
    #region IDisposable Members 

    public void Dispose() 
    { 
     otherVariable = null; 
     if (dataSet != null) 
     { 
      dataSet.Dispose(); 
     } 
    } 

    #endregion 
} 

Antwort

2

Nicht ganz. Wenn Sie Member-Variablen haben, die wegwerfbar sind, sollten Sie es wahrscheinlich so entsorgen. Ihr Objekt kann länger als der Umfang der Arbeit, die es ausführt, leben, da der Garbage Collector nicht garantiert zu einem bestimmten Zeitpunkt ausgeführt wird.

Das Festlegen von verwalteten Variablen auf null ist jedoch Zeitverschwendung. Das Objekt wird nicht schneller GC.

11

Die GC nicht Anruf .Dispose() (Es wird jedoch, rufen Sie die Finalisierung ~MyClass() Methode, die Sie einen Anruf an die Dispose() Methode zur Verfügung stellen kann Ressourcen automatisch verwaltet haben, wenn die GC Ihre Klasse aufzuräumen entscheidet).

Sie müssen immer einen Weg der Entsorgung interne Ressourcen zur Verfügung stellen, wie DataSets zu Code, der Ihre Klassen verwendet (und stellen Sie sicher, .Dispose() tatsächlich anrufen oder den Konstruktor in einem using wickeln). Die Verwendung von IDisposable für Ihre Klassen, die interne Ressourcen verwenden, wird dringend empfohlen.

Von MSDN:

Die primäre Verwendung dieser Schnittstelle ist nicht verwalteten Ressourcen freizugeben. Der Garbage Collector automatisch gibt den Speicher frei, der einem verwalteten Objekt zugewiesen ist, wenn dieses Objekt nicht länger verwendet wird. Es ist jedoch nicht möglich, vorherzusagen, wenn Müll Sammlung auftreten wird. Darüber hinaus der Garbage Collector hat keine Kenntnisse von nicht verwalteten Ressourcen wie Fenster behandelt, oder öffnen Sie Dateien und Streams. Kein

public void Dispose() 
{ 
    otherVariable = null; 
    if (dataSet != null) 
    { 
     dataSet.Dispose(); 
     dataSet = null; 
    } 
} 
6

, Entsorgen Methoden sind keine Zeitverschwendung.

Mit dem Dispo-Muster kann ein Anrufer eine Klasse bereinigen, sobald sie damit fertig ist, anstatt darauf zu warten, dass der GC sie erfasst. Die Verzögerung spielt für den einfachen Heapspeicher keine Rolle, weshalb Basisklassen wie String diese nicht implementieren. Was Dispose aber nützlich ist, ist die Reinigung von unmanaged Ressourcen. An anderer Stelle verwendet die Dataset-Klasse eine nicht verwaltete Ressource. Daher stellt sie eine Methode zur Verfügung, mit der Sie wissen lassen können, wann diese nicht verwaltete Ressource freigegeben werden kann.

Wenn das Muster korrekt befolgt wurde, hat Dataset auch einen Finalizer (oder eine Unterklasse), was bedeutet, dass der Finalizer aufgerufen und nicht verwaltet wird, wenn Sie ihn nicht manuell löschen Ressource würde auf diese Weise aufgeräumt werden. Diese nicht verwaltete Ressource könnte jedoch wichtig sein. Stellen Sie sich vor, wenn es sich um eine Dateisperre oder eine Datenbankverbindung handelt, möchten Sie nicht warten, bis der GC ausgeführt wird, bevor Sie Ihre Datenbankverbindung erneut verwenden können.Dispose bietet eine deterministische Möglichkeit, Ressourcen zu bereinigen, wenn sie fertig sind, anstatt sich auf den nicht-deterministischen GC zu verlassen.

Zum Festlegen von Variablen auf Null in einer Dispose-Methode. In fast allen Fällen wäre es sinnlos. Wenn Sie eine Variable auf null setzen, wird ein Verweis auf diese Variable entfernt, wodurch sie für die Garbage Collection geeignet wird (wenn dies die letzte Referenz ist). Da Sie die Klasse jedoch dennoch verwerfen, werden Sie wahrscheinlich den Bereich für das Containing verlassen Klasse, so dass die interne Klasse trotzdem für die Sammlung in Frage kommt. Wenn Sie Membervariablen in Ihrer Klasse haben, die von Ihnen erstellt wurden (nicht nur Verweise, die Sie behalten), sollten Sie sie immer über die Dispose-Methode Ihrer eigenen Klasse aufrufen, aber setzen Sie sie nicht auf null .

0

Müllwagen kommt jede Woche zu meinem Bereich, aber es sammelt meinen Müll nicht, wenn ich meinen Müllbehälter nicht so aufstellen, dass er sich sammeln kann.

Sie sollten einfach alle unerwünschten Ereignisabonnements entfernen, unmanaged Handler referenzieren und löschen. Dann kümmert sich Garbage Collector um den Rest.

Das folgende Beispiel zeigt die allgemeine Best Practice zur Implementierung der IDisposable-Schnittstelle. Referenz: https://msdn.microsoft.com/en-us/library/system.idisposable.dispose(v=vs.110).aspx

public class DisposeExample 
{ 
    // A base class that implements IDisposable. 
    // By implementing IDisposable, you are announcing that 
    // instances of this type allocate scarce resources. 
    public class MyResource: IDisposable 
    { 
     // Pointer to an external unmanaged resource. 
     private IntPtr handle; 
     // Other managed resource this class uses. 
     private Component component = new Component(); 
     // Track whether Dispose has been called. 
     private bool disposed = false; 

     // The class constructor. 
     public MyResource(IntPtr handle) 
     { 
      this.handle = handle; 
     } 

     // Implement IDisposable. 
     // Do not make this method virtual. 
     // A derived class should not be able to override this method. 
     public void Dispose() 
     { 
      Dispose(true); 
      // This object will be cleaned up by the Dispose method. 
      // Therefore, you should call GC.SupressFinalize to 
      // take this object off the finalization queue 
      // and prevent finalization code for this object 
      // from executing a second time. 
      GC.SuppressFinalize(this); 
     } 

     // Dispose(bool disposing) executes in two distinct scenarios. 
     // If disposing equals true, the method has been called directly 
     // or indirectly by a user's code. Managed and unmanaged resources 
     // can be disposed. 
     // If disposing equals false, the method has been called by the 
     // runtime from inside the finalizer and you should not reference 
     // other objects. Only unmanaged resources can be disposed. 
     protected virtual void Dispose(bool disposing) 
     { 
      // Check to see if Dispose has already been called. 
      if(!this.disposed) 
      { 
       // If disposing equals true, dispose all managed 
       // and unmanaged resources. 
       if(disposing) 
       { 
        // Dispose managed resources. 
        component.Dispose(); 
       } 

       // Call the appropriate methods to clean up 
       // unmanaged resources here. 
       // If disposing is false, 
       // only the following code is executed. 
       CloseHandle(handle); 
       handle = IntPtr.Zero; 

       // Note disposing has been done. 
       disposed = true; 

      } 
     } 

     // Use interop to call the method necessary 
     // to clean up the unmanaged resource. 
     [System.Runtime.InteropServices.DllImport("Kernel32")] 
     private extern static Boolean CloseHandle(IntPtr handle); 

     // Use C# destructor syntax for finalization code. 
     // This destructor will run only if the Dispose method 
     // does not get called. 
     // It gives your base class the opportunity to finalize. 
     // Do not provide destructors in types derived from this class. 
     ~MyResource() 
     { 
      // Do not re-create Dispose clean-up code here. 
      // Calling Dispose(false) is optimal in terms of 
      // readability and maintainability. 
      Dispose(false); 
     } 
    } 
    public static void Main() 
    { 
     // Insert code here to create 
     // and use the MyResource object. 
    } 
} 
+0

Der Zweck von 'IDisposable' ist nicht Ressourcen zu zerstören - es ist * sie zu * freigeben *. Wenn ein 'FileStream' eine Datei öffnet, fordert er an, dass das Betriebssystem bis auf weiteres verhindert, dass jemand anderes die Datei verwendet; Das Aufrufen von 'Dispose' auf dem' FileStream' zerstört die Datei nicht - im Gegenteil, sie macht sie für andere Entitäten * nutzbar. Der GC arbeitet nach dem Prinzip, dass es keine Eile geben sollte, Dinge zu zerstören, wenn Platz vorhanden ist, aber sobald Code für einen 'FileStream' keinen Nutzen mehr hat, sollte er versuchen, die Datei wieder für anderen Code verfügbar zu machen möglich. – supercat

+0

@supercat: Nicht ganz sicher, was Sie sagen wollen. Ich denke, FileStream Dispose gibt den Dateihandler frei und lässt den Garbage Collector verwaltete Instanzen löschen. Es ist der einzige Zweck, wenn es nicht für andere nutzbar gemacht wird. Wenn Sie die Datei nicht ausschließlich öffnen, kann sie möglicherweise von anderen verwendet werden. – CharithJ

+0

Der Grund, warum GC alleine nicht ausreicht, ist, dass 'FileStream' nicht immer eine Dateisperre in einer Weise enthält, die andere Entitäten stört, die die Datei benutzen müssen, aber * manchmal *, und es ist im Allgemeinen einfacher, Ressourcen freizugeben, wenn sie nicht mehr benötigt werden, ohne Rücksicht darauf, ob Konflikte tatsächlich existieren, als Fälle zu identifizieren, in denen Konflikte nicht existieren, und Ressourcen aufzugeben, ohne sie in solchen Fällen freizugeben. Beachten Sie, dass das * manchmal * nicht sehr "oft" sein muss, damit "IDisposable" sich lohnt. Selbst wenn man einen Datei-Stream verlässt ... – supercat

Verwandte Themen