Il garbage collector rilascia automaticamente la memoria allocata per un oggetto gestito quando non è più utilizzato. Comunque non è possibile prevedere quando il garbage collector entrerà in azione. Inoltre il garbage collector non conosce risorse non gestite come ad esempio window handles, streams ecc.
Utilizzando il metodo Dispose di questa interfaccia esplicitamente rilasciamo risorse non gestite in congiunzione con il garbage collector. Chi utilizza un oggetto può chiamare questo metodo quando l'oggetto non è più necessario.
Utilizzando lo using si renderà ancora più semplice ed efficace la chiamata al dispose piuttosto che utilizzare il pattern try/finally per essere sicuri che le risorse non gestite siano disposte anche in presenza di eccezione.
using System;
using System.ComponentModel;
// Il seguente esempio dimostra come creare
// una classe che implementa l'interfaccia IDisposable
// e il metodo IDisposable.Dispose.
public class DisposeExample
{
// Una classe base che implementa IDisposable.
//implementando IDisposable stai dichiarando che le istanze
//allocheranno delle risorse
public class MyResource : IDisposable
{
// Puntatore ad una risorsa non gestita
private IntPtr handle;
//Altra risorsa gestita dalla classe
private Component component = new Component();
// Tracci se il metodo dispose è stato chiamato
private bool disposed = false;
// Il costruttore della classe
public MyResource(IntPtr handle)
{
this.handle = handle;
}
// Implementa IDisposable.
// Non dichiarare il metodo virtuale
// Una classe derivata non è abilitata a fare l'override questo metodo.
public void Dispose()
{
Dispose(true);
// Questo oggetto sarà rilasciato dal metodo dispose
// Inoltre chiamerai GC.SupressFinalize per portare
// fuori dalla coda di finalizzazione e prevenire che
// il codice di finalizzazione per questo oggetto
// sia eseguito una seconda volta
GC.SuppressFinalize(this);
}
// Per Dispose(bool disposing) ci sono due distinti scenari.
// Se disposing è uguale a true il metodo è stato chiamato direttamente
// o indirettamente dal codice dell'utente. Risorse gestite e non possono
// essere disposte.
// Se disposing è uguale a false il metodo è stato chiamato dal runtime
// dall'interno del finalizzatore e non dovresti riferire ad altri oggetti.
// Soltanto risorse non gestite possono essere disposte.
private void Dispose(bool disposing)
{
// Controlla se il dispose è stato chiamato
if (!this.disposed)
{
// Se disposing è uguale a true, disponi tutte le risorse gestite e non
if (disposing)
{
// Dispone risorse gestite.
component.Dispose();
}
// chiama l'appropriato metodo per rilasciare risorse non gestite.
// Se il disposing è uguale a false solo il questo codice è eseguito
CloseHandle(handle);
handle = IntPtr.Zero;
// notifica che il dispose è stato fatto
disposed = true;
}
}
// Usa interop per chiamare il metodo necessario
// per rilasciare risorse non gestite
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);
// Usa la sintassi di C# per i distruttori per finalizzare il codice.
// Questo distruttore viene eseguito solo se il metodo
// dispose non è stato chiamato
// Dà la possibilità la classe base l'opportunità di finalizzare.
// Non fornire distruttori nei tipi derivati da questa classe.
~MyResource()
{
// Chiamando Dispose(false) è ottimale in termini di leggibilità e manutenibilità
Dispose(false);
}
}
public static void Main()
{
IntPtr i = new IntPtr(25);
using (MyResource myResource = new MyResource(i))
{
}
//le risorse dell'istanza sono disposte automaticamente
}
}
In .NET, la gestione degli oggetti COM è fatta mediante il runtime callable wrappers (RCWs), cioè oggetti gestiti che si attuano come oggetti proxy per trattenere gli oggetti com sottostanti.
Il .NET garbage collector rilascia in modo non deterministico gli oggetti gestiti dalla memoria, però nel caso in cui gli oggetti com trattengono risorse di sistema (file handles, connessione al database ecc.) devi esplicitamente rilasciare gli oggetti com per liberare le risorse trattenute da essi.
Per liberare gli oggetti com sotto l'RCW dalla memoria in modo deterministico è possibile utilizzare il ReleaseComObject.
Chiamando il ReleaseComObject decrementeremo il contatore del reference trattenuto dall'rcw. Una volta che il contatore del reference ha raggiunto lo zero (potrebbe essere richiesto chiamare il metodo più volte) l'rcw è segnato per la garbage collection. Se gli oggetti com non trattengo altri oggetti com il runtime com rilascera l'oggetto com stesso.
private void MyFunction()
{
ESRI.ArcGIS.Display.IStyleGallery styCls = new
ESRI.ArcGIS.Framework.StyleGalleryClass() as
ESRI.ArcGIS.Display.IStyleGallery;
// Use the StyleGalleryClass here.
int refsLeft = 0;
do
{
refsLeft = Marshal.ReleaseComObject(styCls);
}
while (refsLeft > 0);
}
Utilizzato oggetti come i cursori che utilizzano risorse è importante rilasciarli non appena si è finito di utilizzarli e non dover aspettare che entri in azione la garbage collector poichè i cursori trattenendo risorse (data source) potrebbero bloccare l'applicazione fino a che non siano rilasciati i cursori.
Si pensi all'sde datasource, i cursori trattengono l'sde stream e se l'applicazione ha tanti client, ognuno potrebbe trattenere un sde stream esaurendo ad esempio il massimo numero consentito di stream.
for (int i = 1; i < 2500; i++)
{
IQueryFilter qu = new QueryFilterClass();
qu.WhereClause = @"Area = " + i.ToString();
IFeatureCursor featCursor = featClass.Search(qu, true);
System.Runtime.InteropServices.Marshal.ReleaseComObject(featCursor);
}
In ArcGis server per assicurare che l'oggetto com sia rilasciato fuori dal suo scopo abbiamo una oggetto di helper chiamato WebObject.
Usa il metodo ManageLifeTime per aggiungere il tuo oggetto com ad un insieme di oggetti che saranno rilasciati esplicitamente quando il webobject è disposto.
using (WebObject webobj = new WebObject())
{
ServerConnection serverConn = new ServerConnection("doug", true);
IServerObjectManager som = serverConn.ServerObjectManager;
IServerContext ctx = som.CreateServerContext("Yellowstone", "MapServer");
IMapServer mapsrv = ctx.ServerObject as IMapServer;
IMapServerObjects mapo = mapsrv as IMapServerObjects;
IMap map = mapo.get_Map(mapsrv.DefaultMapName);
IFeatureLayer flayer = map.get_Layer(0) as IFeatureLayer;
IFeatureClass fclass = flayer.FeatureClass;
IFeatureCursor fcursor = fclass.Search(null, true);
webobj.ManageLifetime(fcursor); //aggiungo il cursore
IFeature f = null;
while ((f = fcursor.NextFeature()) != null)
{
// Do something with the feature.
}
ctx.ReleaseContext();
}
//fuori dallo scopo il cursore è rilasciato poichè l'oggetto webobj è disposto
Il ComReleaser ha la stessa funzione del webObject. Può essere utilizzato con l'engine ( ComReleaser Gestione del ciclo di vita dei cursori ).
Altri oggetti importanti da rilasciare sono i Singletons (oggetti che hanno una sola istanza per processo) per non lasciare processi come pinning cioè pendenti. ( lista oggetti singleton )