Domanda

Di recente ho implementato una classe come:

class TestClass : IDisposable
{
    RegistryKey m_key;
    public TestClass()
    {
        m_key = Registry.CurrentUser.OpenSubKey("Software", false);
    }

    public void Dispose()
    {
        // m_key.Dispose();
        IDisposable disp = m_key;
        disp.Dispose();
    }
}

Se decomprimo la chiamata diretta a Dispose, viene visualizzato l'errore CS0117 (" 'Microsoft.Win32.RegistryKey' non contiene una definizione per 'Dispose' "). Alcuni googling mi hanno portato a questa discussione , dove ho imparato cosa stava succedendo , quindi ora capisco i meccanismi di esso. Il MSDN documentazione suggerisce che l'autore preferirebbe che io chiamassi Close () invece di Dispose (), ma non spiega perché.

Qual è lo scopo di questo modello (che penso di aver visto anche nelle classi IO)? Alla luce del fatto che questa è stata una decisione intenzionale dell'autore della classe, quanto è male il codice sopra (la chiamata a disporre attraverso l'interfaccia IDisposable)? Non può essere così male - dopo tutto, è quello che accadrebbe in una dichiarazione using, giusto?

[modifica: 1) ha cambiato il titolo da " non pubblico " a " esplicito " 2) rimosso l'implementazione esplicita dal mio codice, lasciato accidentalmente fuori dalla sperimentazione]

È stato utile?

Soluzione

Questo si chiama implementazione esplicita dell'interfaccia . Nel tuo esempio poiché definisci il metodo Dispose () come & Quot; void IDisposable.Dispose () & Quot; stai implementando esplicitamente anche l'interfaccia IDisposable.

Questo è normalmente fatto per evitare collisioni. Se Microsoft avesse mai voluto aggiungere un altro metodo Dispose () che avesse fatto qualcos'altro a RegistryKey, non sarebbe stato in grado di farlo se non avessero usato l'implementazione esplicita di tale interfaccia.

Questo viene fatto spesso con il generico IEnumerable < T > interfaccia. Richiede l'implementazione anche dell'interfaccia IEnumerable non generica. L'unico membro di queste due interfacce è GetEnumerator, con quello generico più utile, quindi di solito viene implementato in questo modo:

public clas SomeClass : IEnumerable<SomeOtherClass>
{
    public IEnumerator<SomeOtherClass> GetEnumerator ()
    {
        ...
    }

    IEnumerator IEnumerable.GetEnumerator ()
    {
        return GetEnumerator ();
    }
}

In questo modo quando chiami un oggetto del metodo GetEnumator di SomeClass, chiama la versione generica, poiché l'altra è stata implementata in modo esplicito, permettendoci di ottenere i generici di tipizzazione forte che consentono.

Vedi le pagine 166-169 di Programmazione C # di Jesse Liberty (ho la quarta edizione).

Altri suggerimenti

La maggior parte delle persone non è d'accordo con me, ma mi piace usare l'implementazione esplicita dell'interfaccia per tutte interfacce . Voglio chiarire se sto scrivendo un metodo da chiamare sul mio oggetto o sulla mia interfaccia.

Questo è doloroso se hai un riferimento all'oggetto e vuoi chiamare un metodo di interfaccia (come nell'esempio sopra), ma lo mitigo scrivendo:

class C : IDisposable
{
    public IDisposable IDisposable { get { return this; } }
    void IDisposable.Dispose() { }
}

che significa che chiamare il metodo su C assomiglia a:

C c = ...
c.IDisposable.Dispose();

Il compilatore analizza questo come " Chiama la proprietà IDisposable su C, quindi chiama il metodo Dispose() sul risultato " ma l'ho letto come " Chiama il metodo IDisposable.Dispose() su C " che qui sembra naturale.

Questo approccio può diventare brutto quando si usano interfacce generiche, sfortunatamente.

scroll top