Ich benutze die meiste Zeit die explizite Schnittstellenimplementierung. Hier sind die Hauptgründe.
Refactoring ist sicherer
Wenn Sie eine Schnittstelle ändern, ist es besser, wenn der Compiler dies überprüfen kann. Dies ist bei impliziten Implementierungen schwieriger.
Zwei häufige Fälle kommen in den Sinn:
Hinzufügen einer Funktion zu einer Schnittstelle, bei der eine vorhandene Klasse, die diese Schnittstelle implementiert, bereits eine Methode mit derselben Signatur wie die neue hat . Dies kann zu unerwartetem Verhalten führen und hat mich mehrmals hart gebissen. Es ist schwierig, beim Debuggen "zu sehen", da diese Funktion wahrscheinlich nicht mit den anderen Schnittstellenmethoden in der Datei gefunden wird (das unten erwähnte selbstdokumentierende Problem).
Entfernen einer Funktion von einer Schnittstelle . Implizit implementierte Methoden sind plötzlich toter Code, aber explizit implementierte Methoden werden von Kompilierungsfehlern erfasst. Selbst wenn der tote Code gut zu behalten ist, möchte ich gezwungen sein, ihn zu überprüfen und zu bewerben.
Es ist bedauerlich, dass C # kein Schlüsselwort hat, das uns zwingt, eine Methode als implizite Implementierung zu markieren, sodass der Compiler die zusätzlichen Überprüfungen durchführen kann. Virtuelle Methoden haben aufgrund der erforderlichen Verwendung von "Überschreiben" und "Neu" keines der oben genannten Probleme.
Hinweis: Bei festen oder sich selten ändernden Schnittstellen (normalerweise von Hersteller-APIs) ist dies kein Problem. Für meine eigenen Schnittstellen kann ich jedoch nicht vorhersagen, wann / wie sie sich ändern werden.
Es ist selbstdokumentierend
Wenn ich 'public bool Execute ()' in einer Klasse sehe, ist zusätzliche Arbeit erforderlich, um herauszufinden, dass es Teil einer Schnittstelle ist. Jemand muss es wahrscheinlich kommentieren oder in eine Gruppe anderer Schnittstellenimplementierungen einfügen, alle unter einer Region oder einem Gruppenkommentar mit der Aufschrift "Implementierung von ITask". Das funktioniert natürlich nur, wenn der Gruppenkopf nicht außerhalb des Bildschirms liegt.
Während: 'bool ITask.Execute ()' klar und eindeutig ist.
Klare Trennung der Schnittstellenimplementierung
Ich halte Schnittstellen für „öffentlicher“ als öffentliche Methoden, da sie so gestaltet sind, dass nur ein kleiner Teil der Oberfläche des Betontyps freigelegt wird. Sie reduzieren den Typ auf eine Fähigkeit, ein Verhalten, eine Reihe von Merkmalen usw. Und bei der Implementierung halte ich es für nützlich, diese Trennung beizubehalten.
Wenn ich den Code einer Klasse durchschaue und auf explizite Schnittstellenimplementierungen stoße, wechselt mein Gehirn in den Modus "Codevertrag". Oft werden diese Implementierungen einfach an andere Methoden weitergeleitet, aber manchmal führen sie zusätzliche Status- / Parameterprüfungen, Konvertierungen eingehender Parameter, um den internen Anforderungen besser zu entsprechen, oder sogar Übersetzungen für Versionszwecke durch (dh mehrere Generationen von Schnittstellen, die alle auf gemeinsame Implementierungen zurückgreifen).
(Mir ist klar, dass die Öffentlichkeit auch Codeverträge sind, aber die Schnittstellen sind viel stärker, insbesondere in einer schnittstellengesteuerten Codebasis, in der die direkte Verwendung konkreter Typen normalerweise ein Zeichen für internen Code ist.)
Siehe auch: Grund 2 oben von Jon .
Und so weiter
Plus die Vorteile, die bereits in anderen Antworten hier erwähnt wurden:
Probleme
Es ist nicht alles Spaß und Glück. Es gibt einige Fälle, in denen ich mich an Implizite halte:
- Werttypen, da dies Boxen und geringere Leistung erfordert. Dies ist keine strenge Regel und hängt von der Benutzeroberfläche und deren Verwendung ab. IComparable? Implizit. IFormattable? Wahrscheinlich explizit.
- Triviale Systemschnittstellen mit Methoden, die häufig direkt aufgerufen werden (z. B. IDisposable.Dispose).
Es kann auch schwierig sein, das Casting durchzuführen, wenn Sie tatsächlich den konkreten Typ haben und eine explizite Schnittstellenmethode aufrufen möchten. Ich gehe auf zwei Arten damit um:
- Fügen Sie Öffentlichkeiten hinzu und lassen Sie die Schnittstellenmethoden für die Implementierung an diese weiterleiten. In der Regel mit einfacheren Schnittstellen, wenn intern gearbeitet wird.
- (Meine bevorzugte Methode) Fügen Sie ein
public IMyInterface I { get { return this; } }
(das inline werden sollte) hinzu und rufen Sie auf foo.I.InterfaceMethod()
. Wenn mehrere Schnittstellen diese Fähigkeit benötigen, erweitern Sie den Namen über mich hinaus (meiner Erfahrung nach ist es selten, dass ich diese Notwendigkeit habe).