2009-01-03 10 views
9

I umgesetzt kürzlich eine Klasse wie:Warum explizite Schnittstellenimplementierung?

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(); 
    } 
} 

Wenn ich den direkten Aufruf zu entsorgen Kommentar-, erhalte ich Fehler CS0117 („‚Microsoft.Win32.RegistryKey‘enthält keine Definition für‚entsorgen‘enthalten“). Etwas Googeln führte mich zu this thread, wo ich lernte, was vor sich ging, also verstehe ich jetzt die Mechanik davon. Die MSDN Dokumentation schlägt vor, dass der Autor bevorzugen würde, dass ich Close() anstelle von Dispose() anrufe, aber nicht erklärt warum.

Was ist der Zweck dieses Musters (was ich denke, ich habe es auch in den IO-Klassen gesehen)? Angesichts der Tatsache, dass dies eine absichtliche Entscheidung des Klassenautors war, wie schlimm ist der obige Code (der Aufruf von Dispose über die IDisposable-Schnittstelle)? Es kann nicht so schlimm sein - schließlich ist es das, was in einer Nutzungsanweisung passieren würde, oder?

[Änderungen: 1) geändert Titel von "non-public" auf "explicit" 2) die explizite Implementierung von meinem Code entfernt, zufällig von Experimenten in der linke]

+1

Sie könnten '(m_key als IDisposable) .Dispose();' für Kurzschrift verwenden. –

Antwort

15

Dies wird explizite Schnittstellenimplementierung genannt. Da Sie in Ihrem Beispiel die Methode Dispose() als "void IDisposable.Dispose()" definieren, implementieren Sie die IDisposable-Schnittstelle explizit.

Dies wird normalerweise getan, um Kollisionen zu vermeiden. Wenn Microsoft jemals eine andere Dispose() -Methode hinzufügen wollte, die etwas anderes zu RegistryKey getan hat, wären sie nicht in der Lage, es sei denn, sie hätten eine explizite Implementierung dieser Schnittstelle verwendet.

Dies wird oft mit der generischen IEnumerable < T Schnittstelle durchgeführt. Dazu müssen Sie auch die nicht generische Schnittstelle IEnumerable implementieren. Das einzige Mitglied in diesen beiden Schnittstellen ist GetEnumerator, mit dem generischen einem nützlichen zu sein, so dass sie in der Regel wie folgt umgesetzt:

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

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

diese Weise, wenn Sie ein Objekt von Someclass des GetEnumator Methode aufrufe, ruft die generische Version, da der andere explizit implementiert wurde, erlauben wir es den stark typisierenden Generika zu erlauben.

Siehe Seiten 166-169 von Programmierung C# von Jesse Liberty (ich habe die vierte Ausgabe).

-2

Die meisten Menschen mit mir nicht einverstanden sind, aber ich für alle Schnittstellen explizite Schnittstellenimplementierung mag mit. Ich möchte klarstellen, ob ich eine Methode schreibe, die an meinem Objekt oder an meiner Schnittstelle aufgerufen wird.

Das ist schmerzhaft, wenn Sie einen Verweis auf das Objekt und wollen eine Schnittstelle Methode aufzurufen (wie im obigen Beispiel), aber ich mildere sie durch Schreiben:

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

was bedeutet, dass die Methode Aufruf auf C wie folgt aussieht:

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

der Compiler diese parst als „rufen sie die IDisposable Eigenschaft auf C, dann rufen sie die Dispose() Methode auf das Ergebnis“ aber ich lese es als „rufen sie die IDisposable.Dispose() Methode auf C“ w das scheint hier natürlich zu sein.

Dieser Ansatz kann leider mit generischen Schnittstellen hässlich werden.

+0

Gleiches hier auf den expliziten Schnittstellen. Generika machen es manchmal sehr schwierig. – JaredPar

+32

Eek - explizite Interface-Implementierung macht es nicht nur für den Aufrufer schwieriger, ohne Workarounds wie oben, sondern auch Vererbung - Sie kommen in schwierige Situationen, wenn eine abgeleitete Klasse eine Interface-Methode überschreiben will. Ich vermeide es, wenn ich kann. –

+0

Vererbbare Klassen, die Schnittstellen implementieren, sollten dies entweder mit virtuellen öffentlichen Mitgliedern tun (in den Fällen, in denen die Mitglieder als Klassenmitglieder erreichbar sein sollten) oder mit virtuellen geschützten Mitgliedern, deren Name eine modifizierte Form des Interface-Mitgliedsnamens oder der Signatur ist. VB.NET erlaubt letzteres direkt zu tun 'Protected Overridable Sub Foo_Impl() Implementiert IInterface.Foo', aber in C# ist es das Beste, eine geschützte virtuelle Methode zu definieren, um die" Eingeweide "der Schnittstelle zu halten und die Schnittstelle zu haben Implementation tun nichts anderes als dazu zu ketten. – supercat

Verwandte Themen