È giusto aspettarsi che se una classe C # si occupa di risorse non gestite e implementazioni IDisposable, allora dovrebbe anche implementare qualche tipo di logica finalizzatore?
Abbiamo una classe di utilità fornita dal fornitore che utilizza alcune risorse non gestite. Non abbiamo il codice sorgente, quindi non conosciamo troppi dettagli di implementazione, a parte il fatto che implementa IDisposable ma non ha alcun finalizzatore o SafeHandle. Quindi, senza chiamare Dispose, perde. Codice di esempio semplificato:
public class Utility : IDisposable
{
//This class has some unmanaged resources
...
//Utility function
public void DoSomething(...) { ... }
//IDisposable
public void Dispose() { ... }
protected virtual void Dispose(bool disposing) { ... }
}
Nel nostro codice abbiamo creato una facciata attorno a esso per semplificare l'utilizzo e aggiunto anche un singleton dato che è usato in decine di posti nella nostra complessa base di codice legacy. Esempio:
public class UtilityFacade : IDisposable
{
protected Utility utility = new Utility();
//Simplified utility function
public void DoSomethingGood() { utility.DoSomething(1,2,3); }
//IDisposable
protected bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
utility.Dispose();
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
//Singleton
public static UtilityFacade Instance { get; } = new UtilityFacade();
}
Quindi dalle nostre applicazioni (sito web, servizio web, app console) verrebbe usato in questo modo:
UtilityFacade.Instance.DoSomethingGood();
Il problema è che poiché l'istanza è un membro statico, non verrà mai eliminata tramite IDisposable, quindi la classe Utility non verrà eliminata e, poiché non implementa alcun finalizzatore, perde risorse non gestite alla fine del processo di applicazione.
Quale è la scelta giusta qui:
- Il fornitore dice che dovremmo chiamare Utility.Dispose () al termine dell'applicazione. Ciò richiederebbe ulteriore codifica per noi, ad es. collegarsi ad Application_End per applicazioni web, o aggiungere try {} finally {...} blocks per console apps per la pulizia deterministica, o aggiungere un finalizzatore alla classe facade (che manterrebbe il codice correlato all'utilità in un posto almeno) .
- Chiediamo al fornitore di implementare il finalizzatore. Dato che la classe Utility tratta direttamente con le risorse non gestite, è necessario aggiungere la logica del finalizzatore. Secondo alcuni articoli sulla rete è la migliore pratica raccomandata da Microsoft.
O qualche altro consiglio?