"Abstrakte Klasse" und "Schnittstelle" sind ähnliche Konzepte, wobei die Schnittstelle die abstraktere der beiden ist. Ein entscheidender Faktor ist, dass abstrakte Klassen bei Bedarf Methodenimplementierungen für abgeleitete Klassen bereitstellen. In C # wurde dieser Differenzierungsfaktor jedoch durch die kürzliche Einführung von Erweiterungsmethoden reduziert, die es ermöglichen, Implementierungen für Schnittstellenmethoden bereitzustellen. Ein weiterer Unterscheidungsfaktor ist, dass eine Klasse nur eine abstrakte Klasse erben kann (dh es gibt keine Mehrfachvererbung), aber sie kann mehrere Schnittstellen implementieren. Dies macht Schnittstellen weniger restriktiv und flexibler. Wann sollten wir in C # abstrakte Klassen anstelle von Schnittstellen mit Erweiterungsmethoden verwenden?
Ein bemerkenswertes Beispiel für das Modell der Schnittstelle + Erweiterungsmethode ist LINQ, bei dem die Abfragefunktion für jeden Typ bereitgestellt wird, der IEnumerable
über eine Vielzahl von Erweiterungsmethoden implementiert wird .
quelle
Antworten:
Wenn ein Teil der Klasse implementiert werden muss. Das beste Beispiel, das ich verwendet habe, ist das Template-Methodenmuster .
Auf diese Weise können Sie die Schritte definieren, die beim Aufrufen von Do () ausgeführt werden, ohne die Einzelheiten der Implementierung zu kennen. Ableitende Klassen müssen die abstrakten Methoden implementieren, nicht jedoch die Do () -Methode.
Erweiterungsmethoden erfüllen nicht unbedingt den Teil der Gleichung, der ein Teil der Klasse sein muss. Darüber hinaus können (scheinen) iirc-Erweiterungsmethoden in ihrem Gültigkeitsbereich alles andere als öffentlich sein.
bearbeiten
Die Frage ist interessanter, als ich ursprünglich angenommen hatte. Nach weiterer Prüfung beantwortete Jon Skeet eine solche Frage zu SO zugunsten der Verwendung von Schnittstellen + Erweiterungsmethoden. Ein möglicher Nachteil ist auch die Reflexion gegen eine auf diese Weise entworfene Objekthierarchie.
Persönlich habe ich Probleme, den Nutzen einer Änderung einer derzeit üblichen Praxis zu erkennen, sehe aber auch einige bis gar keine Nachteile, wenn ich das tue.
Es ist zu beachten, dass es über Utility-Klassen möglich ist, auf diese Weise in vielen Sprachen zu programmieren. Erweiterungen stellen nur den syntaktischen Zucker bereit, damit die Methoden so aussehen, als ob sie zur Klasse gehören.
quelle
Do()
definieren, als Erweiterungsmethode definiert. Und die Methoden, die für die Definition der abgeleiteten Klassen übrigDoThis()
bleiben, werden Mitglied der Hauptschnittstelle. Und mit Schnittstellen können Sie mehrere Implementierungen / Vererbungen unterstützen. Und siehe dies- odetocode.com/blogs/scott/archive/2009/10/05/…Es geht darum, was Sie modellieren möchten:
Abstrakte Klassen arbeiten durch Vererbung . Da sie nur spezielle Basisklassen sind, modellieren sie eine Beziehung.
Zum Beispiel ein Hund ist ein Tier, so haben wir
Da es keinen Sinn macht , ein generisches All-Tier zu erstellen (wie würde es aussehen?), Machen wir diese
Animal
Basisklasseabstract
- aber es ist immer noch eine Basisklasse. Und dieDog
Klasse ergibt keinen Sinn, ohne eine zu seinAnimal
.Schnittstellen sind eine andere Geschichte. Sie verwenden keine Vererbung, sondern bieten Polymorphismus (der auch mit Vererbung implementiert werden kann ). Sie modellieren keine Is -A- Beziehung, sondern eher eine, die sie unterstützt .
Nehmen wir zum Beispiel
IComparable
- ein Objekt, das es unterstützt, mit einem anderen verglichen zu werden .Die Funktionalität einer Klasse hängt nicht von den von ihr implementierten Schnittstellen ab. Die Schnittstelle bietet lediglich eine allgemeine Möglichkeit, auf die Funktionalität zuzugreifen. Wir könnten noch
Dispose
eineGraphics
oder eineFileStream
ohne sie umsetzenIDisposable
. Die Schnittstelle verknüpft nur die Methoden miteinander.Im Prinzip kann man Schnittstellen wie Erweiterungen hinzufügen und entfernen, ohne das Verhalten einer Klasse zu ändern, und so den Zugriff darauf verbessern. Sie können jedoch keine Basisklasse entfernen, da das Objekt bedeutungslos werden würde!
quelle
Animal
abstrakt machen . Auf diese Weise können Sieabstract
Funktionen schreiben , die auf die abgeleiteten Klassen spezialisiert sind. Oder mehr in Bezug auf die Programmierung - es spielt wohl keinen Sinn machen , eine zu schaffenUserControl
, sondern als eineButton
ist einUserControl
, so haben wir die gleiche Art von Klasse / Basisklasse Beziehung.Mir ist gerade aufgefallen, dass ich seit Jahren keine abstrakten Klassen mehr verwendet habe (zumindest keine).
Die abstrakte Klasse ist ein merkwürdiges Biest zwischen Schnittstelle und Implementierung. Es gibt etwas in abstrakten Klassen, das meinen Spinnensinn kribbelt. Ich würde argumentieren, man sollte die Implementierung hinter der Schnittstelle in keiner Weise "blenden" (oder irgendwelche Verknüpfungen herstellen).
Selbst wenn Klassen, die eine Schnittstelle implementieren, ein gemeinsames Verhalten benötigen, würde ich statt einer abstrakten Klasse eine unabhängige Hilfsklasse verwenden.
Ich würde für Schnittstellen gehen.
quelle
IMHO, ich denke, dass es hier eine Vermischung von Konzepten gibt.
Klassen verwenden eine bestimmte Art von Vererbung, während Schnittstellen eine andere Art von Vererbung verwenden.
Beide Arten der Vererbung sind wichtig und nützlich, und jede hat ihre Vor- und Nachteile.
Beginnen wir am Anfang.
Die Geschichte mit Interfaces beginnt schon lange mit C ++. Mitte der 1980er Jahre, als C ++ entwickelt wurde, wurde die Idee von Schnittstellen als eine andere Art von Typ noch nicht verwirklicht (sie würde rechtzeitig kommen).
C ++ hatte nur eine Art von Vererbung, die Art der von Klassen verwendeten Vererbung, die als Implementierungsvererbung bezeichnet wird.
Um die GoF Book-Empfehlung anwenden zu können: "Für die Schnittstelle und nicht für die Implementierung programmieren", unterstützte C ++ abstrakte Klassen und Mehrfachimplementierungsvererbung, um in der Lage zu sein, zu tun, wozu Schnittstellen in C # in der Lage (und nützlich!) Sind .
C # führt eine neue Art von Typ, Schnittstellen und eine neue Art von Vererbung, die Schnittstellenvererbung, ein, um mindestens zwei verschiedene Möglichkeiten zur Unterstützung von "So programmieren Sie für die Schnittstelle und nicht für die Implementierung" mit einer wichtigen Einschränkung: Nur C # Unterstützt Mehrfachvererbung mit Schnittstellenvererbung (und nicht mit Implementierungsvererbung).
Die architektonischen Gründe für Schnittstellen in C # sind die Empfehlungen von GoF.
Ich hoffe das kann helfen.
Herzliche Grüße, Gastón
quelle
Das Anwenden von Erweiterungsmethoden auf eine Schnittstelle ist nützlich, um ein allgemeines Verhalten auf Klassen anzuwenden, die möglicherweise nur eine gemeinsame Schnittstelle verwenden. Solche Klassen sind möglicherweise bereits vorhanden und können auf andere Weise für Erweiterungen geschlossen werden.
Für neue Designs würde ich die Verwendung von Erweiterungsmethoden für Schnittstellen bevorzugen. Bei einer Hierarchie von Typen bieten Erweiterungsmethoden eine Möglichkeit, das Verhalten zu erweitern, ohne die gesamte Hierarchie zur Änderung öffnen zu müssen.
Erweiterungsmethoden können jedoch nicht auf die private Implementierung zugreifen. Wenn ich also die private Implementierung an der Spitze einer Hierarchie hinter einer öffentlichen Schnittstelle einkapseln muss, ist eine abstrakte Klasse der einzige Weg.
quelle
Ich denke, dass in C # Mehrfachvererbung nicht unterstützt wird und deshalb wird das Konzept der Schnittstelle in C # eingeführt.
Bei abstrakten Klassen wird die Mehrfachvererbung ebenfalls nicht unterstützt. So ist es einfach zu entscheiden, ob abstrakte Klassen oder Interfaces verwendet werden sollen.
quelle
Ich bin gerade dabei, eine Abstract-Klasse in meinem Projekt zu implementieren, weil C # keine Operatoren (in meinem Fall implizite Konvertierungen) für Interfaces unterstützt.
quelle