Perché l'implementazione esplicita dell'interfaccia?
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]
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.