2010-04-29 28 views
10

Ho visto così tanto codice C# nel mio tempo come sviluppatore che tenta di aiutare il GC insieme impostando le variabili su null o chiamando Dispose() sulle classi (DataSet per esempio) all'interno delle proprie classi Dispose() metodo che Mi sono chiesto se è necessario implementarlo in un ambiente gestito.Come implementare correttamente IDisposable

Questo codice è una perdita di tempo nel suo schema di progettazione?

class MyClass : IDisposable 
{ 
    #region IDisposable Members 

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

    #endregion 
} 

risposta

2

Non interamente. Se hai variabili membro che sono usa e getta, allora probabilmente dovresti eliminarle in questo modo. Il tuo oggetto potrebbe vivere più a lungo dell'ambito del lavoro che sta svolgendo in quanto il garbage collector non è garantito per essere eseguito in un determinato momento.

L'impostazione delle variabili gestite su null è tuttavia una perdita di tempo. L'oggetto non otterrà GC più velocemente.

11

non Il GC fa chiamata .Dispose() (Esso, tuttavia, chiamare il metodo finalizzare ~MyClass(), che è possibile fornire una chiamata al metodo Dispose() di avere risorse gestite automaticamente quando il GC decide di ripulire la classe).

si deve sempre fornire un modo di disporre di risorse interne, come DataSets al codice che utilizza le classi (e garantire effettivamente chiamare .Dispose() o avvolgere il costruttore in un using). Si consiglia vivamente di utilizzare IDisposable per le classi che utilizzano risorse interne.

Da MSDN:

L'uso primario di questa interfaccia è per liberare risorse non gestite. Il garbage collector rilascia automaticamente la memoria assegnata a un oggetto gestito quando tale oggetto non è più utilizzato da . Tuttavia, non è possibile prevedere quando si verificherà la raccolta di immondizia . Inoltre, il garbage collector non ha conoscenze di risorse non gestite come handle di finestre o file aperti e flussi.

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

No, a smaltimento metodi non sono una perdita di tempo.

Il pattern di smaltimento è lì per consentire a un chiamante di ripulire una classe non appena hanno finito con essa, piuttosto che aspettare che il GC la raccolga. Il ritardo non è molto importante per la memoria di heap normale, motivo per cui le classi di base come String non la implementano. Ciò che è utile per Dispose è la pulizia delle risorse non gestite. Da qualche parte internamente, la classe Dataset utilizza una risorsa non gestita, quindi fornisce un metodo di smaltimento per consentire di sapere quando questa risorsa non gestita può essere rilasciata.

Se il modello è stato seguito correttamente Dataset avrà anche un finalizzatore (o qualche sottoclasse volontà) il che significa che se non smaltire manualmente, alla fine il GC correrebbe, il finalizzatore otterrebbe chiamato e non gestito la risorsa verrebbe pulita in questo modo. Questa risorsa non gestita potrebbe essere importante, immagina se si trattasse di un blocco di file o di una connessione a un database, non si desidera rimanere in attesa che il GC venga eseguito prima di poter riutilizzare la connessione al database.Dispose fornisce un metodo deterministico per ripulire le risorse quando sono terminate anziché affidarsi al GC non deterministico.

Come per impostare le variabili su null in un metodo di dispose. Quasi tutti i casi sarebbe inutile. l'impostazione di una variabile su null rimuove un riferimento a tale variabile, che lo renderà idoneo per la garbage collection (se questo è l'ultimo riferimento), ma poiché si sta eliminando la classe comunque, è probabile che si esca dall'ambito per il contenitore classe, quindi la classe interna diventerà comunque eleggibile per la raccolta.

Se all'interno della classe sono presenti variabili membro monouso create dall'utente (non solo riferimenti in attesa), è necessario chiamare sempre su di esse dal metodo di smaltimento della propria classe, ma non preoccuparsi di impostarle su null .

0

Il camion della spazzatura viene nella mia zona ogni settimana ma non raccoglie la mia spazzatura a meno che non metta il cestino della spazzatura in un modo che può raccogliere.

È sufficiente rimuovere tutte le sottoscrizioni di eventi indesiderati, fare riferimento e deselezionare i gestori non gestiti. Allora Garbage Collector si prenderà cura di tutto il resto.

Nell'esempio seguente viene illustrata la procedura ottimale generale per implementare l'interfaccia IDisposable. Riferimento: 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

Lo scopo di "IDisposable" non è quello di distruggere le risorse: è quello di * rilasciarle *. Quando un 'FileStream' apre un file, richiede che il sistema operativo impedisca a chiunque altro di utilizzare il file fino a nuovo avviso; chiamare 'Dispose' su' FileStream' non distrugge il file - al contrario, * lo rende utilizzabile da altre entità *. Il GC opera sul principio che non ci dovrebbe essere fretta di distruggere le cose quando c'è spazio per tenerle, ma una volta che il codice non ha più utilità per un 'FileStream', dovrebbe cercare di rendere il file nuovamente disponibile ad altro codice non appena possibile. – supercat

+0

@supercat: Non sei proprio sicuro di cosa stai cercando di dire. Immagino che FileStream Dispose rilasci il gestore di file e permetta al garbage collector di cancellare le istanze gestite. È l'unico scopo se non renderlo utilizzabile per gli altri. Se non apri il file esclusivamente, potrebbe comunque essere possibile utilizzarlo da altri. – CharithJ

+0

Il motivo per cui GC non è sufficiente per cose come 'FileStream' non è che * sempre * detengono un blocco di file in una maniera che interferisce con altre entità che hanno bisogno di usare il file, ma che * qualche volta * fanno, ed è generalmente più facile rilasciare le risorse quando non sono più necessarie, senza riguardo per l'effettiva esistenza della contesa, piuttosto che identificare i casi in cui la contesa non esisterà e abbandonare le risorse senza rilasciarle in questi casi. Nota che * a volte * non deve essere molto "spesso" per rendere utile 'IDisposable'. Anche se l'abbandono di un flusso di file ... – supercat

Problemi correlati