Condividi tramite


Implementazione di un metodo Dispose

Lo scopo del metodo Dispose di un tipo è quello di rilasciarne tutte le risorse, nonché di rilasciare tutte le risorse appartenenti ai relativi tipi base tramite la chiamata del metodo Dispose del tipo padre. Il metodo Dispose del tipo padre deve rilasciarne tutte le risorse e chiamare a sua volta il metodo Dispose del proprio tipo padre, propagando questo modello in tutta la gerarchia dei tipi base. Al fine di garantire la corretta pulitura delle risorse in ogni occasione, deve essere possibile chiamare il metodo Dispose più volte senza che venga generata un'eccezione.

NoteImportante

Questo argomento non è destinato ai programmatori C++, che possono invece fare riferimento a Destructors and Finalizers in Visual C++. In .NET Framework versione 2.0 il compilatore C++ fornisce il supporto per l'implementazione dell'eliminazione deterministica delle risorse e non consente l'implementazione diretta del metodo Dispose.

Un metodo Dispose dovrebbe chiamare il metodo GC.SuppressFinalize per l'oggetto che viene eliminato. Se l'oggetto si trova nella coda di finalizzazione, GC.SuppressFinalize impedisce che venga chiamato il relativo metodo Finalize. L'esecuzione di un metodo Finalize incide notevolmente sulle prestazioni. Pertanto, se il metodo Dispose ha già eseguito le operazioni di pulitura sull'oggetto, non occorre che il Garbage Collector chiami il relativo metodo Finalize.

Nota

L'esempio di codice fornito per il metodo System.GC.KeepAlive(System.Object) illustra come una procedura di Garbage Collection troppo incisiva possa determinare l'esecuzione di un finalizzatore mentre un membro dell'oggetto recuperato è ancora in esecuzione. È consigliabile chiamare il metodo KeepAlive alla fine di un metodo Dispose di lunga durata.

Nell'esempio di codice riportato di seguito viene illustrato il modello di progettazione consigliato per l'implementazione di un metodo Dispose per le classi che incapsulano risorse non gestite. Questo modello è implementato in .NET Framework.

Le classi di risorse sono solitamente derivate da classi native complesse o da API e devono essere personalizzate di conseguenza. Utilizzare questo modello di codice come punto di partenza per creare classi di risorse e personalizzarle opportunamente in base alle risorse incapsulate. Non è possibile compilare questo esempio e utilizzarlo direttamente in un'applicazione.

La classe base riportata nell'esempio, BaseResource, implementa un metodo Dispose pubblico che può essere chiamato dagli utenti della classe. Questo metodo chiama a propria volta il metodo virtual Dispose(bool disposing), virtual Dispose(disposing As Boolean) in Visual Basic. Viene passato true o false a seconda dell'identità del chiamante. Il codice di pulitura appropriato per l'oggetto viene eseguito nel metodo Dispose.

Dispose(bool disposing) viene eseguito in due scenari distinti. Se disposing è uguale a true, il metodo è stato chiamato direttamente o indirettamente dal codice utente ed è possibile eliminare le risorse gestite e non gestite. Se disposing è uguale a false, il metodo è stato chiamato dal runtime dall'interno del finalizzatore ed è possibile eliminare solo le risorse non gestite. Quando un oggetto esegue il proprio codice di finalizzazione, non deve fare riferimento ad altri oggetti, perché i finalizzatori non vengono eseguiti in un ordine preciso. Se un finalizzatore in esecuzione fa riferimento a un altro oggetto che è già stato finalizzato, la finalizzazione in corso non riuscirà.

La classe base fornisce un metodo Finalize o un distruttore come misura di sicurezza nel caso in cui il metodo Dispose non venga chiamato. Il metodo Finalize chiama il metodo Dispose che accetta parametri, passando false. È preferibile non ricreare il codice di pulitura di Dispose all'interno del metodo Finalize. La chiamata di Dispose(false) è ottimale per la leggibilità e la gestibilità del codice.

La classe MyResourceWrapper illustra come eseguire la derivazione da una classe che implementa la gestione delle risorse mediante Dispose. MyResourceWrapper esegue l'override del metodo virtual Dispose(bool disposing) e fornisce il codice di pulitura per le risorse gestite e non gestite create. MyResourceWrapper chiama inoltre Dispose sulla relativa classe base BaseResource affinché la base sia in grado di eseguire correttamente la pulitura. La classe derivata MyResourceWrapper non dispone di un metodo Finalize o Dispose privo di parametri, perché li eredita dalla classe base BaseResource.

Nota

Nel metodo protected Dispose(bool disposing) riportato nell'esempio non viene applicata la funzionalità di thread-safe, poiché il metodo non può essere chiamato contemporaneamente da un thread dell'utente e da un thread del finalizzatore. In un'applicazione client che utilizza BaseResource, inoltre, è opportuno non consentire mai la chiamata simultanea al metodo protected Dispose(bool disposing) da parte di più thread dell'utente. Un'applicazione o una libreria di classi deve essere progettata in modo da consentire a un solo thread di disporre della durata di una risorsa e di chiamare Dispose quando la risorsa non è più necessaria. A seconda della risorsa, l'accesso non sincronizzato dei thread durante l'eliminazione delle risorse può determinare un rischio in termini di protezione. Gli sviluppatori devono quindi esaminare attentamente il codice per determinare l'approccio ottimale per l'applicazione della protezione dei thread.

' Design pattern for the base class.
' By implementing IDisposable, you are announcing that instances
' of this type allocate scarce resources.
Public Class BaseResource
   Implements IDisposable
   ' Pointer to an external unmanaged resource.
   Private handle As IntPtr 
   ' Other managed resource this class uses.
   Private Components As Component
   ' Track whether Dispose has been called.
   Private disposed As Boolean = False

   ' Constructor for the BaseResource Object.
   Public Sub New()
      ' Insert appropriate constructor code here.
   End Sub

   ' Implement IDisposable.
   ' Do not make this method Overridable.
   ' A derived class should not be able to override this method.
   Public Overloads Sub Dispose()Implements IDisposable.Dispose
      Dispose(true)
      ' Take yourself off of the finalization queue
      ' to prevent finalization code for this object
      ' from executing a second time.
      GC.SuppressFinalize(Me) 
   End Sub

' Dispose(disposing As Boolean) executes in two distinct scenarios.
' If disposing is 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 Overloads Overridable Sub Dispose(disposing As Boolean)
   ' Check to see if Dispose has already been called.
   If Not (Me.disposed) Then
      ' If disposing equals true, dispose all managed 
      ' and unmanaged resources.
      If (disposing) Then
         ' Dispose managed resources.
         Components.Dispose()
      End If
      ' Release unmanaged resources. If disposing is false,
      ' only the following code is executed.      
      CloseHandle(handle)
      handle = IntPtr.Zero
      ' Note that this is not thread safe.
      ' Another thread could start disposing the object
      ' after the managed resources are disposed,
      ' but before the disposed flag is set to true.
      ' If thread safety is necessary, it must be
      ' implemented by the client.
   End If
   Me.disposed = true
End Sub

   ' This Finalize method will run only if the 
   ' Dispose method does not get called.
   ' By default, methods are NotOverridable. 
   ' This prevents a derived class from overriding this method.
   Protected Overrides Sub Finalize()
         ' Do not re-create Dispose clean-up code here.
         ' Calling Dispose(false) is optimal in terms of
         ' readability and maintainability.
         Dispose(false)
   End Sub
   
   ' Allow your Dispose method to be called multiple times,
   ' but throw an exception if the object has been disposed.
   ' Whenever you do something with this class, 
   ' check to see if it has been disposed.
   Public Sub DoSomething()
      If Me.disposed Then
         Throw New ObjectDisposedException()
      End if
   End Sub
End Class

' Design pattern for a derived class.
' Note that this derived class inherently implements the 
' IDisposable interface because it is implemented in the base class.
Public Class MyResourceWrapper
   Inherits BaseResource
   
   ' A managed resource that you add in this derived class.
   private addedManaged As ManagedResource
   ' A native unmanaged resource that you add in this derived class.
   private addedNative As NativeResource
   ' Track whether Dispose has been called.
   Private disposed As Boolean = False

   ' Constructor for the MyResourceWrapper object.
   Public Sub New()      
      MyBase.New()
      ' Insert appropriate constructor code here for the
      ' added resources.
   End Sub

   Protected Overloads Overrides Sub Dispose(disposing As Boolean)
      If Not (Me.disposed) Then
         Try
            If disposing Then
              ' Release the managed resources you added in
              ' this derived class here.
              addedManaged.Dispose()
            End If
            ' Release the native unmanaged resources you added
            ' in this derived class here.
            CloseHandle(addedNative)
            Me.disposed = true
         Finally
            ' Call Dispose on your base class.
            MyBase.Dispose(disposing)
         End Try
      End If
   End Sub
End Class
' This derived class does not have a Finalize method
' or a Dispose method without parameters because it 
' inherits them from the base class.
// Design pattern for the base class.
// By implementing IDisposable, you are announcing that instances
// of this type allocate scarce resources.
public class BaseResource: IDisposable
{
   // Pointer to an external unmanaged resource.
   private IntPtr handle;
   // Other managed resource this class uses.
   private Component Components;
   // Track whether Dispose has been called.
   private bool disposed = false;

   // Constructor for the BaseResource object.
   public BaseResource()
   {
      // Insert appropriate constructor code here.
   }

   // Implement IDisposable.
   // Do not make this method virtual.
   // A derived class should not be able to override this method.
   public void Dispose()
   {
      Dispose(true);
      // Take yourself off the Finalization queue 
      // to 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.
            Components.Dispose();
         }
         // Release unmanaged resources. If disposing is false, 
         // only the following code is executed.
         CloseHandle(handle);
         handle = IntPtr.Zero;
         // Note that this is not thread safe.
         // Another thread could start disposing the object
         // after the managed resources are disposed,
         // but before the disposed flag is set to true.
         // If thread safety is necessary, it must be
         // implemented by the client.

      }
      disposed = true;         
   }

   // 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.
   ~BaseResource()      
   {
      // Do not re-create Dispose clean-up code here.
      // Calling Dispose(false) is optimal in terms of
      // readability and maintainability.
      Dispose(false);
   }

   // Allow your Dispose method to be called multiple times,
   // but throw an exception if the object has been disposed.
   // Whenever you do something with this class, 
   // check to see if it has been disposed.
   public void DoSomething()
   {
      if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}

// Design pattern for a derived class.
// Note that this derived class inherently implements the 
// IDisposable interface because it is implemented in the base class.
public class MyResourceWrapper: BaseResource
{
   // A managed resource that you add in this derived class.
   private ManagedResource addedManaged;
   // A native unmanaged resource that you add in this derived class.
   private NativeResource addedNative;
   private bool disposed = false;

  // Constructor for this object.
   public MyResourceWrapper()
   {
      // Insert appropriate constructor code here.
   }

   protected override void Dispose(bool disposing)
   {
      if(!this.disposed)
      {
         try
         {
            if(disposing)
            {
               // Release the managed resources you added in
               // this derived class here.
               addedManaged.Dispose();         
            }
            // Release the native unmanaged resources you added
            // in this derived class here.
            CloseHandle(addedNative);
            this.disposed = true;
         }
         finally
         {
            // Call Dispose on your base class.
            base.Dispose(disposing);
         }
      }
   }
}

// This derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits 
// them from the base class.

Implementazione di un metodo Close

Per alcuni tipi risulta più naturale chiamare un metodo Close anziché un metodo Dispose. In questi casi è possibile aggiungere al tipo base un metodo Close pubblico. Il metodo Close chiama a propria volta il metodo Dispose senza parametri, che esegue le operazioni di pulitura appropriate. Nell'esempio di codice riportato di seguito viene illustrato un metodo Close.

' Do not make this method Overridable.
' A derived class should not be allowed
' to override this method.
Public Sub Close()
   ' Calls the Dispose method without parameters.
   Dispose()
End Sub
// Do not make this method virtual.
// A derived class should not be allowed
// to override this method.
public void Close()
{
   // Calls the Dispose method without parameters.
   Dispose();
}

Vedere anche

Riferimenti

GC.SuppressFinalize Method
Destructors and Finalizers in Visual C++
Implementazione dei metodi Finalize e Dispose per la pulitura delle risorse non gestite

Concetti

Override del metodo Finalize