Ich verstehe, wie man mit Interfaces und der expliziten Implementierung von Interfaces in C # arbeitet, aber ich habe mich gefragt, ob es als schlechte Form angesehen wird, bestimmte Member zu verstecken, die nicht häufig verwendet werden. Beispielsweise:
public interface IMyInterface
{
int SomeValue { get; set; }
int AnotherValue { get; set; }
bool SomeFlag { get; set; }
event EventHandler SomeValueChanged;
event EventHandler AnotherValueChanged;
event EventHandler SomeFlagChanged;
}
Ich weiß im Voraus, dass die Ereignisse zwar nützlich, aber sehr selten genutzt werden würden. Daher hielt ich es für sinnvoll, sie durch explizite Implementierung vor einer implementierenden Klasse zu verbergen, um IntelliSense-Unordnung zu vermeiden.
c#
interfaces
implementations
Kyle Baran
quelle
quelle
Antworten:
Ja, es ist im Allgemeinen eine schlechte Form.
Wenn Ihr IntelliSense zu überladen ist, ist Ihre Klasse zu groß. Wenn Ihre Klasse zu groß ist, macht sie wahrscheinlich zu viele Dinge und / oder ist schlecht gestaltet.
Beheben Sie Ihr Problem, nicht Ihr Symptom.
quelle
Verwenden Sie die Funktion für das, wofür sie vorgesehen war. Das Reduzieren von Namespace-Unordnung ist keine davon.
Bei legitimen Gründen geht es nicht darum, sich zu "verstecken" oder sich zu organisieren, sondern Mehrdeutigkeiten aufzulösen und sich zu spezialisieren. Wenn Sie zwei Schnittstellen implementieren, die "Foo" definieren, kann sich die Semantik für Foo unterscheiden. Wirklich kein besserer Weg, um dies zu lösen, außer für explizite Schnittstellen. Die Alternative besteht darin, das Implementieren überlappender Schnittstellen zu verbieten oder zu deklarieren, dass Schnittstellen keine Semantik zuordnen können (was ohnehin nur eine konzeptionelle Sache ist). Beides sind schlechte Alternativen. Manchmal übertrifft die Zweckmäßigkeit die Eleganz.
Einige Autoren / Experten betrachten es als einen Trick eines Features (die Methoden sind nicht wirklich Teil des Objektmodells des Typs). Aber wie alle Funktionen, irgendwo braucht es jemand.
Auch hier würde ich es nicht zum Verstecken oder Organisieren verwenden. Es gibt Möglichkeiten, Mitglieder vor Intellisense zu verbergen.
quelle
IDisposable.Dispose
etwas ausgeführt wird, aber ein anderer Name zugewiesen wirdClose
, nicht mag , würde ich es beispielsweise für eine Klasse als angemessen erachten, deren Vertrag ausdrücklich besagt, dass Code Instanzen erstellen und aufgeben kann, ohne dieDispose
explizite Schnittstellenimplementierung für zu verwenden Definieren Sie eineIDisposable.Dispose
Methode, die nichts tut.Ich mag ein Zeichen von chaotischem Code sein, aber manchmal ist die reale Welt chaotisch. Ein Beispiel aus .NET Framework ist ReadOnlyColection, das den mutierenden Setter [], Add und Set verbirgt.
IE ReadOnlyCollection implementiert IList <T>. Siehe auch meine Frage an SO vor einigen Jahren, als ich darüber nachdachte.
quelle
ConcurrentDictionary
, verschiedene Member vonIDictionary<T>
explizit so zu implementieren , dass Konsumenten vonConcurrentDictionary
A) sie nicht direkt aufrufen und B) Methoden verwenden können, die eine Implementierung erfordern,IDictionary<T>
falls dies unbedingt erforderlich ist. Zum Beispiel der NutzerConcurrentDictionary<T>
sollten anrufen ,TryAdd
anstattAdd
unnötige Ausnahmen notwendig zu vermeiden.Add
explizite Implementierung auch das Durcheinander verringert.TryAdd
kann alles, wasAdd
kann (Add
wird als Aufruf von implementiertTryAdd
, vorausgesetzt, beide wären redundant). Hat jedochTryAdd
notwendigerweise einen anderen Namen / eine andere Signatur, so ist eine ImplementierungIDictionary
ohne diese Redundanz nicht möglich . Die explizite Implementierung behebt dieses Problem auf saubere Weise.Es ist eine gute Form, dies zu tun, wenn das Mitglied im Kontext sinnlos ist. Wenn Sie beispielsweise eine schreibgeschützte Auflistung erstellen, die
IList<T>
durch Delegieren an ein internes Objekt implementiert wird,_wrapped
haben Sie möglicherweise Folgendes :Hier haben wir vier verschiedene Fälle.
public T this[int index]
wird eher von unserer Klasse als von der Schnittstelle definiert und ist daher natürlich keine explizite Implementierung. Beachten Sie jedoch, dass es demT this[int index]
in der Schnittstelle definierten Lese- / Schreibzugriff ähnelt, jedoch schreibgeschützt ist.T IList<T>.this[int index]
ist explizit, weil ein Teil davon (der Getter) perfekt mit der obigen Eigenschaft übereinstimmt und der andere Teil immer eine Ausnahme auslöst. Während dies für jemanden wichtig ist, der über die Schnittstelle auf eine Instanz dieser Klasse zugreift, ist es für jemanden, der sie über eine Variable des Klassentyps verwendet, sinnlos.Da
bool ICollection<T>.IsReadOnly
immer true zurückgegeben wird, ist es völlig sinnlos, Code zu verwenden, der für den Typ der Klasse geschrieben wurde, kann jedoch für die Verwendung über den Typ der Schnittstelle von entscheidender Bedeutung sein. Daher implementieren wir ihn explizit.Wird dagegen
public int Count
nicht explizit implementiert, da es möglicherweise für jemanden von Nutzen sein kann, der eine Instanz über ihren eigenen Typ verwendet.Aber mit Ihrem "sehr selten verwendeten" Fall würde ich mich sehr stark dafür einsetzen, keine explizite Implementierung zu verwenden.
In den Fällen, in denen ich die Verwendung einer expliziten Implementierung empfehle, die die Methode über eine Variable des Klassentyps aufruft, wäre dies entweder ein Fehler (Versuch, den indizierten Setter zu verwenden) oder sinnlos (Überprüfung eines Werts, der immer derselbe ist) Sie schützen den Benutzer vor fehlerhaftem oder nicht optimalem Code. Dies unterscheidet sich erheblich von Code, von dem Sie glauben, dass er nur selten verwendet wird. Dafür könnte ich erwägen, das
EditorBrowsable
Attribut zu verwenden, um das Mitglied vor dem Verstand zu verbergen, obwohl selbst ich es müde wäre; Die Gehirne der Menschen haben bereits eine eigene Software, mit der sie herausfiltern können, was sie nicht interessiert.quelle
IsReadOnly
Eigenschaft enthalten ?