Durata degli oggetti: come creare e distruggere oggetti

Aggiornamento: novembre 2007

È stata creata un'istanza di una classe, un oggetto, mediante la parola chiave New. Prima di utilizzare i nuovi oggetti per la prima volta, è spesso necessario eseguire attività di inizializzazione. Tra le attività di inizializzazione più comuni vi sono l'apertura dei file, la connessione a un database e la lettura dei valori delle chiavi del Registro di sistema. In Visual Basic l'inizializzazione di nuovi oggetti viene controllata tramite routine definite costruttori, vale a dire metodi speciali che consentono il controllo dell'inizializzazione.

Dopo che un oggetto abbandona un ambito, viene rilasciato da Common Language Runtime (CLR). In Visual Basic il rilascio delle risorse di sistema viene controllato mediante routine denominate distruttori. Sia i costruttori che i distruttori supportano la creazione di librerie di classi prevedibili e affidabili.

Sub New e Sub Finalize

In Visual Basic le routine Sub New e Sub Finalize consentono di inizializzare ed eliminare in modo permanente gli oggetti e sostituiscono i metodi Class_Initialize e Class_Terminate utilizzati in Visual Basic 6.0 e nelle versioni precedenti. A differenza del metodo Class_Initialize, il costruttore Sub New può essere eseguito solo una volta al momento della creazione della classe e non può essere chiamato in modo esplicito se non dalla prima riga di codice in un altro costruttore o nella stessa classe o in una classe derivata. Inoltre, il codice nel metodo Sub New viene sempre eseguito prima di qualsiasi altro codice in una classe. In Visual Basic 2005 e nelle versioni successive, se non si definisce in modo esplicito una routine Sub New per una classe, in fase di esecuzione viene creato implicitamente un costruttore Sub New.

Prima di rilasciare oggetti, CLR richiede automaticamente al metodo Finalize gli oggetti che definiscono una routine Sub Finalize. È possibile che il metodo Finalize contenga codice che è necessario eseguire subito prima dell'eliminazione permanente di un oggetto, ad esempio codice relativo alla chiusura di file e al salvataggio delle informazioni sullo stato. L'esecuzione di Sub Finalize comporta una lieve riduzione delle prestazioni. Si consiglia quindi di definire un metodo Sub Finalize solo quando è necessario rilasciare in modo esplicito gli oggetti.

Nota:

Il Garbage Collector in CLR non elimina, e non può eliminare, gli oggetti non gestiti, vale a dire gli oggetti che vengono eseguiti direttamente dal sistema operativo all'esterno dell'ambiente CLR. Questo perché è necessario eliminare i vari oggetti non gestiti in modi differenti. Le informazioni non sono direttamente associate all'oggetto non gestito e devono quindi essere identificate nella documentazione relativa all'oggetto. Se una classe utilizza oggetti non gestiti, è necessario eliminarli nel relativo metodo Finalize.

Il distruttore Finalize è un metodo protetto che può essere chiamato solo dalla classe a cui appartiene o dalle classi derivate. Poiché Finalize viene chiamato automaticamente dal sistema quando viene eliminato in modo permanente un oggetto, si consiglia di non chiamarlo in modo esplicito dall'esterno dell'implementazione Finalize di una classe derivata.

A differenza del metodo Class_Terminate, che viene eseguito subito dopo aver impostato un oggetto su Nothing, tra l'abbandono dell'ambito da parte di un oggetto e la chiamata del distruttore Finalize da parte di Visual Basic si verifica generalmente un ritardo. Visual Basic 2005 e versioni successive supporta un altro tipo di distruttore, Dispose, il quale può essere chiamato in modo esplicito in qualsiasi momento per rilasciare subito le risorse.

Nota:

Un distruttore Finalize non deve generare eccezioni, perché queste non possono essere gestite dall'applicazione e possono provocarne l'interruzione.

Interfaccia IDisposable

Le istanze di classe controllano spesso risorse non gestite da CLR, quali gli handle di Windows e le connessioni al database. È necessario eliminare queste risorse in modo permanente nel metodo Finalize della classe per consentirne il rilascio quando il Garbage Collector rimuove l'oggetto. Tuttavia, poiché il Garbage Collector elimina gli oggetti in modo permanente solo quando CLR richiede la disponibilità di maggiore memoria, è possibile che le risorse vengano rilasciate solo molto tempo dopo l'abbandono dell'ambito da parte dell'oggetto.

A supporto del Garbage Collector, le classi possono implementare l'interfaccia IDisposable per fornire un sistema di gestione attiva delle risorse di sistema. IDisposable dispone del metodo Dispose che i client chiamano dopo aver utilizzato un oggetto. È possibile utilizzare il metodo Dispose per rilasciare immediatamente risorse ed eseguire attività quali la chiusura di file e le connessioni al database. A differenza del distruttore Finalize, il metodo Dispose non viene chiamato automaticamente. Quando si desidera rilasciare immediatamente risorse, è necessario che i client di una classe chiamino in modo esplicito il metodo Dispose.

Implementazione di IDisposable

Una classe che implementa l'interfaccia IDisposable deve includere il codice seguente:

  • Campo che consente di controllare se l'oggetto è stato eliminato in modo permanente:

    Protected disposed As Boolean = False
    
  • Overload del metodo Dispose che consente di liberare le risorse della classe. Questo metodo deve essere chiamato dai metodi Dispose e Finalize della classe base:

    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposed Then
            If disposing Then
                ' Insert code to free managed resources.
            End If
            ' Insert code to free unmanaged resources.
        End If
        Me.disposed = True
    End Sub
    
  • Implementazione di Dispose che contiene solo il codice seguente:

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
    
  • Override del metodo Finalize che contiene solo il codice seguente:

    Protected Overrides Sub Finalize()
        Dispose(False)
        MyBase.Finalize()
    End Sub
    

Derivazione da una classe che implementa IDisposable

Non è necessario eseguire l'override di tutti i metodi di base in una classe derivata da una classe base che implementa l'interfaccia IDisposable, a meno che tale classe non utilizzi risorse aggiuntive che devono essere eliminate in modo permanente. In questo caso, la classe derivata deve eseguire l'override del metodo Dispose(disposing) della classe base per eliminare in modo permanente le risorse della classe derivata. Inoltre, tale override deve chiamare il metodo Dispose(disposing) della classe base.

Protected Overrides Sub Dispose(ByVal disposing As Boolean)
    If Not Me.disposed Then
        If disposing Then
            ' Insert code to free managed resources.
        End If
        ' Insert code to free unmanaged resources.
    End If
    MyBase.Dispose(disposing)
End Sub

Una classe derivata non deve eseguire l'override del metodi Dispose e Finalize della classe base. Infatti, quando questi metodi vengono chiamati da un'istanza della classe derivata, la relativa implementazione della classe base chiama l'override del metodo Dispose(disposing) della classe derivata.

Visualizzazione.

Nel diagramma seguente sono riportati i metodi ereditati e i metodi sottoposti a override nella classe derivata.

Rappresentazione grafica eredità Disposable

Se si segue questo schema DisposeFinalize, le risorse della classe derivata e della classe base verranno eliminate correttamente in modo permanente. Nel diagramma seguente sono riportati i metodi chiamati quando le classi vengono eliminate in modo permanente e finalizzate.

Rappresentazione grafica della chiamata di Disposable

Garbage Collection e il distruttore Finalize

In .NET Framework viene utilizzato il sistema Garbage Collection basata su analisi dei riferimenti che prevede il rilascio periodico delle risorse non utilizzate. Per la gestione delle risorse, in Visual Basic 6.0 e nelle versioni precedenti veniva utilizzato un sistema diverso, definito conteggio dei riferimenti. Benché in entrambi i casi venga effettuata automaticamente la stessa funzione, vi sono alcune importanti differenze.

Mediante il metodo CLR gli oggetti vengono eliminati periodicamente quando il sistema stabilisce che non sono più necessari. Gli oggetti vengono rilasciati più rapidamente quando le risorse di sistema sono insufficienti e con una frequenza minore in caso contrario. Il ritardo tra il momento in cui un oggetto abbandona l'ambito e il relativo rilascio da parte di CLR indica che, a differenza di quanto avveniva in Visual Basic 6.0 e nelle versioni precedenti, non è possibile stabilire esattamente quando l'oggetto verrà eliminato in modo permanente. In una situazione di questo tipo si dice che gli oggetti hanno una durata non deterministica. Nella maggior parte dei casi la durata non deterministica non influisce sulla modalità di scrittura delle applicazioni, purché si ricordi che è possibile che il distruttore Finalize non venga eseguito immediatamente dopo la perdita di ambito di un oggetto.

Un'altra differenza tra i sistemi di Garbage Collection riguarda l'utilizzo di Nothing. Per poter utilizzare il conteggio dei riferimenti, in Visual Basic 6.0 e nelle versioni precedenti, a volte veniva assegnato Nothing alle variabili oggetto in modo da rilasciare i riferimenti contenuti in tali variabili. Se la variabile conteneva l'ultimo riferimento all'oggetto, le risorse dell'oggetto venivano rilasciate immediatamente. Anche se in alcuni casi questa routine può risultare ancora utile, la sua esecuzione nelle versioni successive di Visual Basic non risulta mai nel rilascio immediato delle risorse da parte dell'oggetto a cui si fa riferimento. Per rilasciare subito le risorse, utilizzare il metodo Dispose dell'oggetto, se disponibile. Si consiglia di impostare una variabile su Nothing solo nel caso in cui la durata della variabile risulti lunga in relazione al tempo necessario per l'individuazione degli oggetti isolati tramite le operazioni del Garbage Collector.

Vedere anche

Attività

Procedura: implementare il modello DisposeFinalize (Visual Basic)

Concetti

Inizializzazione e terminazione di componenti

Metodi Finalize e distruttori

Riferimenti

Utilizzo di costruttori e distruttori

New (Visual Basic)

Dispose

Nothing (Visual Basic)

Altre risorse

Modifiche al linguaggio per gli utenti di Visual Basic 6.0