Domanda

Stavo leggendo questo l'altro giorno e mi chiedevo perché ci fosse un finalizzatore insieme al metodo Dispose. Ho letto qui su SO sul motivo per cui potresti voler aggiungere Dispose al finalizzatore. La mia curiosità è: quando verrà chiamato Finalizer sul metodo Dispose stesso? C'è un esempio di codice o si basa su qualcosa che sta accadendo nel sistema in cui è in esecuzione il software? In tal caso, cosa potrebbe succedere se il metodo Dispose non viene eseguito dal GC.

È stato utile?

Soluzione

Lo scopo del finalizzatore qui è semplicemente una precauzione di sicurezza contro le perdite di memoria (se ti capita di non chiamare esplicitamente Dispose ). Significa anche che non è necessario disporre degli oggetti se si desidera che rilascino risorse quando il programma si arresta, poiché il GC sarà costretto a finalizzare e raccogliere comunque tutti gli oggetti.

Come punto correlato, è importante disporre l'oggetto in modo leggermente diverso quando lo si fa dal finalizzatore.

~MyClass()
{
    Dispose(false);
}

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

protected void Dispose(disposing)
{
    if (!this.disposed)
    {
        if (disposing)
        {
            // Dispose managed resources here.
        }
        // Dispose unmanaged resources here.
    }
    this.disposed = true;
}

Il motivo per cui non vuoi disporre delle risorse gestite nel tuo finalizzatore è che in questo modo creeresti forti riferimenti a loro e ciò potrebbe impedire al GC di svolgere correttamente il proprio lavoro e collezionandoli. Le risorse non gestite (ad es. Handle Win32 e simili) dovrebbero sempre essere esplicitamente chiuse / eliminate, poiché il CLR non ne è a conoscenza.

Altri suggerimenti

Questo è principalmente lì per proteggerti. Non puoi dettare cosa farà l'utente finale della tua classe. Fornendo un finalizzatore oltre a un metodo di smaltimento, il GC "smaltirà". del tuo oggetto, liberando le tue risorse in modo appropriato, anche se l'utente dimentica di chiamare Dispose () o usa male la tua classe.

Il finalizzatore viene chiamato quando l'oggetto viene garbage collection. Dispose deve essere chiamato esplicitamente. Nel codice seguente verrà chiamato il finalizzatore ma non il metodo Dispose.

class Foo : IDisposable
{
  public void Dispose()
  {
    Console.WriteLine("Disposed");
  }

  ~Foo()
  {
    Console.WriteLine("Finalized");
  }
}

...

public void Go()
{
  Foo foo = new Foo();
}

Il metodo dispose deve essere esplicitamente chiamato, chiamando Dispose () o avendo l'oggetto in un'istruzione using. Il GC chiamerà sempre il finalizzatore, quindi se c'è qualcosa che deve accadere prima che gli oggetti vengano eliminati dal finalizzatore, dovrebbe almeno controllare per assicurarsi che tutto nell'oggetto sia ripulito.

Volete evitare di ripulire gli oggetti nel finalizzatore, se possibile, perché causa lavoro extra rispetto allo smaltimento in anticipo (come chiamare dispose), ma dovreste sempre controllare almeno nel finalizzatore se ci sono oggetti che giacciono intorno che devono essere rimossi.

Una nota importante ma sottile non ancora menzionata: uno scopo raramente considerato di Dispose è quello di impedire che un oggetto venga ripulito prematuramente. Gli oggetti con finalizzatori devono essere scritti con cura, per evitare che un finalizzatore funzioni prima del previsto. Un finalizzatore non può essere eseguito prima dell'inizio dell'ultima chiamata del metodo che verrà effettuata su un oggetto (*), ma a volte potrebbe eseguire durante l'ultima chiamata del metodo se l'oggetto verrà abbandonato una volta che il metodo è completo. Il codice che smaltisce correttamente un oggetto non può abbandonare l'oggetto prima di chiamare Dispose, quindi non c'è pericolo che un finalizzatore provochi il caos sul codice che utilizza correttamente Dispose. D'altra parte, se l'ultimo metodo per utilizzare un oggetto fa uso di entità che verranno ripulite nel finalizzatore dopo l'ultimo utilizzo del riferimento dell'oggetto stesso, è possibile che garbage-collector chiami Finalizza sull'oggetto e ripulisca su entità che sono ancora in uso. Il rimedio è garantire che qualsiasi metodo di chiamata che utilizza entità che verranno ripulite da un finalizzatore debba essere seguito a un certo punto da una chiamata di metodo che faccia uso di "questo". GC.KeepAlive (questo) è un buon metodo da usare per questo.

(*) I metodi non virtuali che vengono espansi in codice in-line che non fa nulla con l'oggetto possono essere esenti da questa regola, ma Dispose di solito è, o invoca, un metodo virtuale.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top