Ziemlich einfach. Ich implementiere eine Schnittstelle, aber es gibt eine Eigenschaft, die für diese Klasse nicht erforderlich ist und eigentlich nicht verwendet werden sollte. Meine anfängliche Idee war, einfach etwas zu tun wie:
int IFoo.Bar
{
get { raise new NotImplementedException(); }
}
Ich nehme an, dass daran nichts auszusetzen ist, aber es fühlt sich nicht "richtig" an. Hat jemand eine ähnliche Situation schon einmal erlebt? Wenn ja, wie sind Sie damit umgegangen?
c#
design
interfaces
implementations
Chris Pratt
quelle
quelle
Antworten:
Dies ist ein klassisches Beispiel dafür, wie Menschen gegen das Liskov-Subtitutionsprinzip verstoßen. Ich rate nachdrücklich davon ab, würde aber möglicherweise eine andere Lösung vorschlagen:
Wenn das erste für Sie zutrifft, implementieren Sie die Schnittstelle einfach nicht in dieser Klasse. Denken Sie daran , wie eine Steckdose , wo der Boden Loch ist nicht erforderlich , so dass es nicht tatsächlich zu Boden befestigen. Sie stecken nichts mit Masse in und keine große Sache! Aber sobald Sie etwas benutzen, das einen Boden braucht, könnten Sie einen spektakulären Fehlschlag erleben. Besser nicht ein Fake-Ground-Loch hineinstecken. Wenn Ihre Klasse also nicht wirklich das tut, was die Schnittstelle vorsieht, implementieren Sie die Schnittstelle nicht.
Hier sind ein paar kurze Abschnitte aus Wikipedia:
Das Liskov-Substitutionsprinzip kann einfach wie folgt formuliert werden: "Stärken Sie keine Vorbedingungen und schwächen Sie keine Nachbedingungen".
Für die semantische Interoperabilität und Substituierbarkeit zwischen verschiedenen Implementierungen derselben Verträge müssen sich alle auf dasselbe Verhalten festlegen.
Das Prinzip der Schnittstellensegregation basiert auf der Idee, dass Schnittstellen in zusammenhängende Mengen unterteilt werden sollten, sodass Sie keine Schnittstelle benötigen, die viele unterschiedliche Aufgaben ausführt, wenn Sie nur eine Einrichtung benötigen . Denken Sie noch einmal an die Schnittstelle einer Steckdose, es könnte auch einen Thermostat haben, aber es würde die Installation einer Steckdose erschweren und die Verwendung für nicht beheizte Zwecke erschweren. Wie eine Steckdose mit Thermostat sind große Schnittstellen schwer zu implementieren und zu verwenden.
quelle
Sieht für mich gut aus, wenn dies Ihre Situation ist.
Es scheint mir jedoch, dass Ihre Schnittstelle (oder deren Verwendung) kaputt ist, wenn eine ableitende Klasse nicht wirklich alles implementiert. Teilen Sie diese Schnittstelle auf.
Haftungsausschluss: Dies erfordert eine mehrfache Vererbung, und ich habe keine Ahnung, ob C # dies unterstützt.quelle
Ich bin auf diese Situation gestoßen. In der Tat, wie an anderer Stelle erwähnt, hat die BCL solche Fälle ... Ich werde versuchen, bessere Beispiele und einige Gründe zu liefern:
Wenn Sie eine bereits ausgelieferte Schnittstelle haben, die Sie aus Kompatibilitätsgründen aufbewahren und ...
Die Schnittstelle enthält Mitglieder, die veraltet oder unleserlich sind. Zum Beispiel
BlockingCollection<T>.ICollection.SyncRoot
(unter anderem), währendICollection.SyncRoot
per se nicht veraltet ist, wird es werfenNotSupportedException
.Die Schnittstelle enthält Elemente, von denen dokumentiert ist, dass sie optional sind und dass die Implementierung möglicherweise die Ausnahme auslöst. Zum Beispiel auf MSDN
IEnumerator.Reset
dazu heißt es:Aus Versehen sollte es sich bei der Gestaltung der Benutzeroberfläche in erster Linie um mehr als eine Benutzeroberfläche handeln. In BCL ist es ein gängiges Muster, schreibgeschützte Versionen von Containern mit zu implementieren
NotSupportedException
. Ich habe es selbst gemacht, es ist das, was jetzt erwartet wird ... Ich kehreICollection<T>.IsReadOnly
zurück,true
damit du es ihnen erzählen kannst. Das richtige Design wäre gewesen, eine lesbare Version der Schnittstelle zu haben, und dann wird die vollständige Schnittstelle von dieser übernommen.Es gibt keine bessere Benutzeroberfläche. Ich habe zum Beispiel eine Klasse, mit der Sie über den Index auf Elemente zugreifen können. Überprüfen Sie, ob ein Element enthalten ist und in welchem Index es eine bestimmte Größe hat. Sie können es in ein Array kopieren. Es scheint ein Job zu sein,
IList<T>
aber meine Klasse hat Eine feste Größe, und das Hinzufügen oder Entfernen wird nicht unterstützt, daher funktioniert es eher wie ein Array als wie eine Liste. Aber es gibt keineIArray<T>
in der BCL .Die Schnittstelle gehört zu einer API, die auf mehrere Plattformen portiert ist, und bei der Implementierung einer bestimmten Plattform werden einige Teile davon nicht unterstützt. Im Idealfall gibt es eine Möglichkeit, dies zu erkennen, sodass portabler Code, der eine solche API verwendet, entscheiden kann, ob diese Teile aufgerufen werden oder nicht
NotSupportedException
. Dies gilt insbesondere dann, wenn es sich um eine Portierung auf eine neue Plattform handelt, die im ursprünglichen Design nicht vorgesehen war.Überlegen Sie auch, warum es nicht unterstützt wird?
Manchmal
InvalidOperationException
ist eine bessere Option. Eine weitere Möglichkeit zum Hinzufügen von Polymorphismus in einer Klasse besteht beispielsweise darin, eine interne Schnittstelle auf verschiedene Weise zu implementieren, und Ihr Code wählt die zu instanziierende Schnittstelle in Abhängigkeit von den im Konstruktor der Klasse angegebenen Parametern aus. [Dies ist besonders nützlich, wenn Sie wissen, dass der Optionssatz feststeht und Sie nicht zulassen möchten, dass Klassen von Drittanbietern durch Abhängigkeitsinjektion eingeführt werden.] Ich habe dies getan, um ThreadLocal zurück zu portieren, da es sich um eine Tracking- und eine Nicht-Tracking-Implementierung handelt zu weit appart, und was wirkt sichThreadLocal.Values
auf die Non-Tracking-Implementierung aus?InvalidOperationException
auch wenn es nicht vom Zustand des Objekts abhängt. In diesem Fall stellte ich die Klasse selbst vor und wusste, dass diese Methode implementiert werden musste, indem nur eine Ausnahme ausgelöst wurde.Manchmal ist ein Standardwert sinnvoll. Zum Beispiel ist es aus den
ICollection<T>.IsReadOnly
oben genannten Gründen sinnvoll, nur "wahr" oder "falsch" zurückzugeben, je nach Fall. Also ... was ist die Semantik vonIFoo.Bar
? Möglicherweise gibt es einen sinnvollen Standardwert, der zurückgegeben werden muss.Nachtrag: Wenn Sie die Kontrolle über die Benutzeroberfläche haben (und aus Kompatibilitätsgründen nicht bei dieser bleiben müssen), sollte es keinen Fall geben, in den Sie werfen müssen
NotSupportedException
. Möglicherweise müssen Sie die Schnittstelle in zwei oder mehr kleinere Schnittstellen aufteilen, um die richtige Passform für Ihren Fall zu finden, was in Extremsituationen zu "Verschmutzung" führen kann.quelle
Ja, die .Net-Bibliotheksdesigner haben es getan. Die Dictionary-Klasse macht das, was Sie tun. Es verwendet eine explizite Implementierung, um einige der IDictionary-Methoden effektiv auszublenden . Hier wird es besser erklärt , aber um zusammenzufassen, um die Dictionary-Methoden Add, CopyTo oder Remove zu verwenden, die KeyValuePairs verwenden, müssen Sie das Objekt zuerst in ein IDictionary umwandeln.
* Es ist nicht "versteckt" die Methoden im engeren Sinne des Wortes "verstecken", wie Microsoft es verwendet . Aber ich kenne in diesem Fall keinen besseren Begriff.
quelle
Sie können immer nur einen harmlosen Wert oder einen Standardwert implementieren und zurückgeben. Immerhin ist es nur eine Eigenschaft. Es kann 0 (die Standardeigenschaft von int) oder einen beliebigen in Ihrer Implementierung sinnvollen Wert (int.MinValue, int.MaxValue, 1, 42 usw.) zurückgeben.
Eine Ausnahme auszulösen scheint eine schlechte Form zu sein.
quelle