Domanda

Ho una classe astratta che implementa IDisposable, in questo modo:

public abstract class ConnectionAccessor : IDisposable
{
    public abstract void Dispose();
}

In Visual Studio 2008 Team System, ho eseguito Analisi del Codice sul mio progetto e una delle avvertenze, che è venuto è il seguente:

Microsoft.Design :Modificare 'ConnectionAccessor.Dispose()' così che si chiama Smaltire(vero), quindi chiama GC.SuppressFinalize su istanza dell'oggetto corrente ('questo' o 'Me' in Visual Basic), e poi torna.

È solo di essere stupido, che mi dice di modificare il corpo di un metodo astratto, o devo fare qualcosa di più in qualsiasi derivato istanza di Dispose?

È stato utile?

Soluzione

Si dovrebbe seguire lo schema tradizionale per l'attuazione Dispose.Fare Dispose() il virtuale è considerata un male, perché lo schema tradizionale sottolinea il riutilizzo di codice in "gestito" pulizia (API client chiamante Dispose() direttamente o per il tramite using) e "non gestito" pulizia (GC chiamata finalizzatore).Per ricordare, il modello è questo:

public class Base
{
    ~Base()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // so that Dispose(false) isn't called later
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
             // Dispose all owned managed objects
        }

        // Release unmanaged resources
    }
}

La chiave qui è che non c'è alcuna duplicazione tra finalizzatore e Dispose per non gestito i lavori di pulizia e di sicurezza di qualsiasi classe derivata può estendere gestito e pulizia.

Per il vostro caso, ciò che si dovrebbe fare è questo:

protected abstract void Dispose(bool disposing)

e lasciare tutto come è.Anche questo è di dubbio valore, dal momento che si stanno applicando derivata classi per implementare Dispose ora - e come fai a sapere che tutti loro hanno bisogno?Se la classe base non ha nulla da smaltire, ma la maggior parte delle classi derivate probabilmente fare (con poche eccezioni, forse), quindi basta fornire un vuoto di attuazione.È quello che System.IO.Stream (di per sé astratto), quindi c'è un precedente.

Altri suggerimenti

L'avviso dice fondamentalmente per implementare il Smaltire il modello nella tua classe.

Il codice risultante dovrebbe essere simile a questo:

public abstract class ConnectionAccessor : IDisposable
{
    ~ConnectionAccessor()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
    }
}

L'unico cruccio che mi piacerebbe avere con le risposte fornite finora è che essi presuppongono che si bisogno per avere un finalizzatore, che non è necessariamente il caso.C'è abbastanza significativo delle prestazioni del carico di lavoro associato con la finalizzazione, che io non voglio imporre a tutte le mie classi derivate se non era necessario.

Vedere questo post del blog da Joe Duffy, che spiega quando si può o non può avere bisogno di un finalizzatore, e come implementare correttamente il modello Dispose in entrambi i casi.
Riassumendo Joe post sul blog, a meno che si sta facendo qualcosa di abbastanza di basso livello trattare con la memoria non gestita, non si deve implementare un finalizzatore.Come regola generale di pollice, se la classe contiene solo riferimenti a tipi gestiti che implementa IDisposable se stessi, non avete bisogno di un finalizzatore (ma dovrebbe implementa IDisposable e disporre di tali risorse).Se l'allocazione di risorse non gestite direttamente dal codice (PInvoke?) e queste risorse deve essere liberata, avete bisogno di uno.Una classe derivata può sempre aggiungere un finalizzatore se è davvero bisogno, ma costringendo tutte le classi derivate di avere un finalizzatore da mettere nella classe base fa sì che tutte le classi derivate di essere influenzati dal calo di prestazioni di oggetti finalizzabili quando il sovraccarico potrebbe non essere necessaria.

Mentre sembra un po ' come nit-picking, il consiglio è valido.Si sta già ad indicare che ci si aspetta di eventuali sub-tipi di ConnectionAccessor avrà qualcosa che hanno bisogno di smaltire.Pertanto, sembra meglio per garantire che la propria pulitura è fatto (in termini di GC.SuppressFinalize chiamata) da classe base, piuttosto che fare affidamento su ciascun sottotipo di farlo.

Io uso il modello dispose citato in Bruce The book Efficace C# che è fondamentalmente:

public class BaseClass : IDisposable
{
    private bool _disposed = false;
    ~BaseClass()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        _disposed = true;
    }
}

public void Derived : BaseClass
{
    private bool _disposed = false;

    protected override void Dispose(bool disposing)
    {
        if (_disposed) 
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        base.Dispose(disposing);
        _disposed = true;
    }

L'avviso è interessante però.Eric Lippert, uno di C# designer, bloggato su perché i messaggi di errore dovrebbe essere "Diagnostica ma non prescrittivo:descrivere il problema, non la soluzione".Leggi qui.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top