Determinare se IDisposable dovrebbe estendere un’interfaccia o essere implementata su una class che implementa detta interfaccia

Come posso determinare se dovrei estendere una delle mie interfacce con IDisposable o implementare IDisposable su una class che implementa la mia interfaccia?

Ho un’interfaccia che non ha bisogno di disporre di risorse esterne, ad eccezione di una particolare implementazione. Le mie opzioni sembrano essere:

1) Implementare IDisposable sull’interfaccia che richiede tutte le implementazioni per implementare Dispose, anche se solo un metodo vuoto.

-o-

2) Implementare IDisposable solo sulle classi che hanno risorse che devono essere smaltite. Ciò causerà problemi con “using” perché il mio object viene creato da una factory e quindi tutto il codice upstream funziona contro l’interfaccia. Poiché l’interfaccia non è vincasting a IDisposable, “using” non vede il metodo Dispose. Tuttavia, potrei trasmettere il risultato di fabbrica all’implementazione; tuttavia, ciò rende il consumatore consapevole dell’implementazione, vanificando lo scopo delle interfacce.

    Qualche idea sulle migliori pratiche?

    Se si prevede che i chiamanti siano in grado di interagire con l’interfaccia e mai l’implementazione, allora si desidera che l’interfaccia sia estesa per IDisposable . In caso contrario, dovranno verificare se il value is IDisposable comunque value is IDisposable per vedere se è necessario eliminarlo.

    Se l’object responsabile per lo smaltimento dell’object conosce l’implementazione concreta, e solo gli oggetti a cui vengono forniti riferimenti (ma non sono responsabili della sua eliminazione) che utilizzano l’interfaccia, considerano la seconda opzione.

    Un buon esempio della prima opzione è IEnumerator . Molti oggetti IEnumerator non hanno bisogno di fare nulla quando sono disposti, ma alcuni lo fanno, e così l’interfaccia estende IDisposable perché l’object responsabile della creazione / ciclo di vita di quell’object non avrà (o dovrebbe) mai avere conoscenza dell’implementazione sottostante .

    Un esempio del secondo sarebbe qualcosa come IComparer molti oggetti che devono essere confrontati sono usa e getta, ma le sezioni di codice che usano un object attraverso l’interfaccia non sono responsabili della sua creazione / ciclo di vita, quindi non ha bisogno di sapere se quel tipo è usa e getta.

    La domanda da $ 50.000 è se la responsabilità della dismissione verrà mai passata insieme all’interfaccia o, per dirla in altro modo, se l’ultima quadro che utilizza un object di implementazione possa essere qualcosa di diverso dall’entity framework che la crea.

    La grande ragione per cui IEnumerator implementa IDisposable è che gli oggetti di implementazione sono creati da oggetti che implementano IEnumerable.GetEnumerator() ma sono generalmente utilizzati da altri oggetti. L’object che implementa IEnumerable saprà se la cosa che restituisce ha davvero bisogno di essere smaltita, ma non avrà modo di sapere quando il destinatario ha finito. Il codice che chiama IEnumerable.GetEnumerator() saprà quando viene eseguito con l’object restituito, ma non avrà modo di sapere se è necessaria una pulitura. La cosa sensata da fare è specificare che il codice che chiama IEnumerable.GetEnumerator() è necessario per assicurare che l’object restituito abbia Dispose chiamato su di esso prima che venga abbandonato; il metodo Dispose in molti casi non farà nulla, ma chiamando incondizionatamente un metodo do-nothing che è garantito esistere è più economico rispetto alla verifica dell’esistenza di un metodo che non esiste.

    Se la natura del proprio tipo di interfaccia è tale che le domande relative alla necessità di una ripulitura di un object di implementazione e quando si verificherà tale pulizia saranno entrambe rispondenti dalla stessa entity framework, quindi non è necessario che l’interfaccia erediti IDisposable . Se le istanze verranno create da un’entity framework ma l’ultima sarà utilizzata da un’altra, l’ereditarietà IDisposable sarebbe saggia.

    Se si implementa IDisposable sulla class concreta e gli utenti dell’interfaccia sanno che potrebbe essere Disposable; tu puoi fare

     IFoo foo = Factory.Create("type"); using(foo as IDisposable){ foo.bar(); } 

    Se foo non implementa IDisposable , l’ using equivarrà a using(null) che funzionerà bene.

    L’output del programma di esempio sottostante sarà

     Fizz.Bar Fizz.Dispose Buzz.Bar 

    Programma di esempio

     using System; internal interface IFoo { void Bar(); } internal class Fizz : IFoo, IDisposable { public void Dispose() { Console.WriteLine("Fizz.Dispose"); } public void Bar() { Console.WriteLine("Fizz.Bar"); } } internal class Buzz : IFoo { public void Bar() { Console.WriteLine("Buzz.Bar"); } } internal static class Factory { public static IFoo Create(string type) { switch (type) { case "fizz": return new Fizz(); case "buzz": return new Buzz(); } return null; } } public class Program { public static void Main(string[] args) { IFoo fizz = Factory.Create("fizz"); IFoo buzz = Factory.Create("buzz"); using (fizz as IDisposable) { fizz.Bar(); } using (buzz as IDisposable) { buzz.Bar(); } Console.ReadLine(); } }