2012-01-20 14 views
10

Ho un'implementazione di un'interfaccia e quell'interfaccia estende IDisposable. Nella mia particolare implementazione dell'interfaccia, non ho bisogno di gettare nulla, quindi ho solo un metodo vuoto Dispose().Come si "implementa" correttamente Dispose() (secondo FxCop) quando l'implementazione è un metodo vuoto? (CA1063)

public interface IMyStuff : IDisposable 
{ 
} 

public MyStuffImpl : IMyStuff 
{ 
    public void Dispose() 
    { 
    } 
} 

Ora, in FxCop, questo si traduce in un CA1063:

Error, Certainty 95, for ImplementIDisposableCorrectly 
{ 
    Resolution : "Provide an overridable implementation of Dispose(
        bool) on 'MyStuffImpl' or mark the type as sealed. 
        A call to Dispose(false) should only clean up native 
        resources. A call to Dispose(true) should clean up 
        both managed and native resources." 
} 
CriticalWarning, Certainty 75, for CallGCSuppressFinalizeCorrectly 
{ 
    Resolution : "Change 'MyStuffImpl.Dispose()' to call 'GC.SuppressFinalize(
        object)'. This will prevent derived types that introduce 
        a finalizer from needing to re-implement 'IDisposable' 
        to call it." 
} 
Error, Certainty 95, for ImplementIDisposableCorrectly 
{ 
    Resolution : "Modify 'MyStuffImpl.Dispose()' so that it 
        calls Dispose(true), then calls GC.SuppressFinalize 
        on the current object instance ('this' or 'Me' in Visual 
        Basic), and then returns." 
} 

Così, sembra che posso risolvere questo in uno dei 2 modi:


rendere la classe sealed :

public sealed MyStuffImpl : IMyStuff 
{ 
    public void Dispose() 
    { 
    } 
} 

attuare parte del modello tipico:

public MyStuffImpl : IMyStuff 
{ 
    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    private void Dispose(bool disposing) 
    { 
    } 
} 

Nel mio caso, non ho intenzione su questa implementazione mai in fase di estensione, quindi io probabilmente risolverlo rendendola sealed, ma ammetto Non capisco davvero perché è importante se è sigillato o meno.

Inoltre, solo perché la mia classe è sigillata, FxCop non mi dice più che Dispose() dovrebbe chiamare GC.SupressFinalize(this); ma è proprio vero? È "migliore" in .NET chiamare sempre SupressFinalize in Dispose, indipendentemente?

+0

Forse la tua interfaccia non dovrebbe implementare IDisposable se hai cose che implementano la tua interfaccia che non hanno bisogno di smaltimento. Puoi implementare IDisposable * in aggiunta * alla tua interfaccia, se necessario. –

+0

@DBM OP sta implementando un'altra interfaccia che eredita IDisposable. 'IEnumerator ' è un esempio. – phoog

+1

@DBM: Se la maggior parte delle implementazioni è disponibile, questa interfaccia dovrebbe essere anche, per incoraggiare gli utenti dell'interfaccia a disporre correttamente. – SLaks

risposta

10

SuppressFinalize() non ha senso a meno che l'istanza non abbia un finalizzatore.
Se la tua classe non ha un finalizzatore, ma non è sealed, dovresti comunque SuppressFinalize, nel caso in cui una classe ereditata aggiunga un finalizzatore.

Entrambe le opzioni sono corrette, tranne che Dispose(bool) deve essere protected virtual.

+1

In realtà, la seconda opzione non è corretta perché l'overload 'Dispose (bool)' non è sovrascrivibile. +1 per sottolineare che 'SuppressFinalize' non ha senso sugli oggetti senza finalizzatore. – phoog

+0

C'è qualche caso in cui una classe dovrebbe aggiungere un finalizzatore di cleanup a una classe base che non è progettata per uno, e c'è qualche ragione per cui una classe non possa gestire GC.SuppressFinalize nel proprio metodo Dispose? Capisco che può essere utile per le classi avere finalizzatori il cui scopo è semplicemente di registrare un errore da eliminare, ma tentare di ripulire un finalizzatore quando la classe base non è progettata per farlo sembra pericoloso e sbagliato. Incapsulare un'istanza di una classe finalizzabile sembrerebbe molto più sicuro. – supercat

+0

@supercat: se la classe derivata aggiunge risorse non gestite. La classe derivata non può sovrascrivere 'Dispose()'. (anche se non sono sicuro del motivo per cui non può chiamare 'SupressFinalize()' in 'Dispose (true)') – SLaks

1

Nella vostra opzione "attuare parte del modello tipico", si dovrebbe rendere il vostro metodo di Dispose(bool)protected virtual:

protected virtual void Dispose(bool disposing) 
{ 
} 

che fornirà sottoclassi l'opportunità di gestire lo smaltimento di tutte le risorse che gestiscono. Questo è il significato di "overridable" in "Fornire un'implementazione overridable di Dispose (bool)"

Ovviamente, public virtual soddisferebbe anche FxCop.

+0

Buon punto. Forse FxCop dovrebbe avvertirmi anche di questo;) – CodingWithSpike

+0

@ rally25rs Ma FxCop ti ha avvertito di questo, quando ha detto "fornire un'implementazione overridable di Dispose (bool)". L'avviso è scomparso quando hai fornito un'implementazione non eliminabile di Dispose (bool)? Se è così, è solo strano. – phoog

+0

Sì, nel mio secondo esempio in cui Dispose() è privato, non vi è alcun avviso FxCop. – CodingWithSpike

Problemi correlati