Was genau ist ein guter Anwendungsfall für die explizite Implementierung einer Schnittstelle?
Ist es nur so, dass Benutzer der Klasse nicht alle diese Methoden / Eigenschaften in Intellisense betrachten müssen?
Wenn Sie zwei Schnittstellen implementieren, beide mit derselben Methode und unterschiedlichen Implementierungen, müssen Sie diese explizit implementieren.
public interface IDoItFast
{
void Go();
}
public interface IDoItSlow
{
void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
void IDoItFast.Go()
{
}
void IDoItSlow.Go()
{
}
}
Es ist nützlich, das nicht bevorzugte Mitglied auszublenden. Wenn Sie beispielsweise beides implementieren
IComparable<T>
undIComparable
es normalerweise besser ist, dieIComparable
Überlastung zu verbergen, um nicht den Eindruck zu erwecken, dass Sie Objekte verschiedener Typen vergleichen können. In ähnlicher Weise sind einige Schnittstellen nicht CLS-kompatibel.IConvertible
Wenn Sie die Schnittstelle also nicht explizit implementieren, können Endbenutzer von Sprachen, die CLS-Konformität erfordern, Ihr Objekt nicht verwenden. (Was sehr katastrophal wäre, wenn die BCL-Implementierer die IConvertible-Mitglieder der Grundelemente nicht verstecken würden :))Ein weiterer interessanter Hinweis ist, dass die Verwendung eines solchen Konstrukts normalerweise bedeutet, dass Strukturen, die eine Schnittstelle explizit implementieren, diese nur durch Boxen an den Schnittstellentyp aufrufen können. Sie können dies umgehen, indem Sie generische Einschränkungen verwenden:
Boxt kein Int, wenn Sie eines an ihn übergeben.
quelle
string
war schon vor Generika und diese Praxis war in Mode. Als .net 2 auf den Markt kam, wollten sie die öffentliche Schnittstelle von .net nicht unterbrechen,string
also ließen sie es so, wie es mit dem Schutz war.Einige zusätzliche Gründe, eine Schnittstelle explizit zu implementieren:
Abwärtskompatibilität : Falls sich die
ICloneable
Schnittstelle ändert, müssen implementierende Methodenklassenmitglieder ihre Methodensignaturen nicht ändern.sauberer Code : Wenn die
Clone
Methode aus ICloneable entfernt wird, tritt ein Compilerfehler auf. Wenn Sie die Methode jedoch implizit implementieren, können nicht verwendete "verwaiste" öffentliche Methoden auftretenStarke Typisierung : Um die Geschichte von Supercat anhand eines Beispiels zu veranschaulichen, wäre dies mein bevorzugter Beispielcode. Die
ICloneable
explizite Implementierung ermöglicht eine starke Typisierung,Clone()
wenn Sie ihn direkt alsMyObject
Instanzmitglied aufrufen :quelle
interface ICloneable<out T> { T Clone(); T self {get;} }
. Beachten Sie, dass es absichtlich keineICloneable<T>
Einschränkung für T gibt. Während ein Objekt im Allgemeinen nur dann sicher geklont werden kann, wenn seine Basis vorhanden ist, möchte man möglicherweise von einer Basisklasse ableiten, die sicher ein Objekt einer Klasse klonen kann, die dies nicht kann. Um dies zu ermöglichen, würde ich empfehlen, dass vererbbare Klassen keine öffentliche Klonmethode verfügbar machen. Verwenden Sie stattdessen vererbbare Klassen mit einerprotected
Klonmethode und versiegelte Klassen, die von ihnen abgeleitet sind und das öffentliche Klonen verfügbar machen.Eine andere nützliche Technik besteht darin, dass die öffentliche Implementierung einer Methode durch eine Funktion einen Wert zurückgibt, der spezifischer ist als in einer Schnittstelle angegeben.
Beispielsweise kann ein Objekt implementieren
ICloneable
, aber seine öffentlich sichtbareClone
Methode muss dennoch einen eigenen Typ zurückgeben.Ebenso
IAutomobileFactory
könnte a eineManufacture
Methode haben, die ein zurückgibtAutomobile
, aber aFordExplorerFactory
, die implementiertIAutomobileFactory
, könnte seineManufacture
Methode a zurückgeben lassenFordExplorer
(was von abgeleitet istAutomobile
). Code, der weiß, dass er eine hat,FordExplorerFactory
könnteFordExplorer
-spezifische Eigenschaften für ein Objekt verwenden, das von a zurückgegeben wird,FordExplorerFactory
ohne typisiert werden zu müssen, während Code, der lediglich wusste, dass er eine Art von hat,IAutomobileFactory
seine Rückgabe einfach alsAutomobile
.quelle
Dies ist auch nützlich, wenn Sie zwei Schnittstellen mit demselben Mitgliedsnamen und derselben Signatur haben, das Verhalten jedoch je nach Verwendung ändern möchten. (Ich empfehle nicht, Code wie diesen zu schreiben):
quelle
public class Animal : Cat, Dog
Es kann die öffentliche Schnittstelle sauberer halten, um eine Schnittstelle explizit zu implementieren, dh Ihre
File
Klasse kannIDisposable
explizit implementieren und eine öffentliche Methode bereitstellen,Close()
die für einen Verbraucher möglicherweise sinnvoller ist alsDispose(
).F # bietet nur eine explizite Schnittstellenimplementierung, sodass Sie immer auf die jeweilige Schnittstelle umstellen müssen, um auf deren Funktionalität zuzugreifen. Dies führt zu einer sehr expliziten (kein Wortspiel beabsichtigten) Verwendung der Schnittstelle.
quelle
Dispose
sind diejenigen, die niemals bereinigt werden müssen); Ein besseres Beispiel wäre so etwas wie die Implementierung einer unveränderlichen Sammlung vonIList<T>.Add
.Wenn Sie eine interne Schnittstelle haben und die Mitglieder in Ihrer Klasse nicht öffentlich implementieren möchten, würden Sie sie explizit implementieren. Implizite Implementierungen müssen öffentlich sein.
quelle
Ein weiterer Grund für die explizite Implementierung ist die Wartbarkeit .
Wenn eine Klasse "beschäftigt" wird - ja, es kommt vor, wir haben nicht alle den Luxus, den Code anderer Teammitglieder umzugestalten -, macht eine explizite Implementierung deutlich, dass eine Methode vorhanden ist, um einen Schnittstellenvertrag zu erfüllen.
So wird die "Lesbarkeit" des Codes verbessert.
quelle
#region
ist dies mit einer entsprechenden Titelzeichenfolge vorgesehen. Und ein Kommentar zur Methode.Ein anderes Beispiel ist das
System.Collections.Immutable
, in dem die Autoren die Technik gewählt haben, um eine vertraute API für Sammlungstypen beizubehalten, während die Teile der Schnittstelle entfernt wurden, die für ihre neuen Typen keine Bedeutung haben.Konkret
ImmutableList<T>
implementiertIList<T>
und somitICollection<T>
( um eineImmutableList<T>
einfachere Verwendung mit Legacy-Code zu ermöglichen),void ICollection<T>.Add(T item)
macht jedoch keinen Sinn für aImmutableList<T>
: Da das Hinzufügen eines Elements zu einer unveränderlichen Liste die vorhandene Liste nicht ändern darf,ImmutableList<T>
leitet sich auch davon ab, fürIImmutableList<T>
wenIImmutableList<T> Add(T item)
verwendet werden kann unveränderliche Listen.Im Fall von sehen
Add
die Implementierungen alsoImmutableList<T>
wie folgt aus:quelle
Bei explizit definierten Schnittstellen sind alle Methoden automatisch privat. Sie können ihnen keinen öffentlichen Zugriffsmodifikator zuweisen. Annehmen:
quelle
So können wir eine explizite Schnittstelle erstellen: Wenn wir zwei Schnittstellen haben und beide Schnittstellen dieselbe Methode haben und eine einzelne Klasse diese beiden Schnittstellen erbt, wurde der Compiler beim Aufrufen einer Schnittstellenmethode verwirrt, welche Methode aufgerufen werden soll Verwalten Sie dieses Problem mithilfe der expliziten Schnittstelle. Hier ist ein Beispiel, das ich unten gegeben habe.
quelle