it-swarm-eu.dev

Wann sollten abstrakte Klassen anstelle von Schnittstellen mit Erweiterungsmethoden in C # verwendet werden?

"Abstrakte Klasse" und "Schnittstelle" sind ähnliche Konzepte, wobei die Schnittstelle die abstraktere von beiden ist. Ein Unterscheidungsmerkmal ist, dass abstrakte Klassen bei Bedarf Methodenimplementierungen für abgeleitete Klassen bereitstellen. In C # wurde dieser Differenzierungsfaktor jedoch durch die kürzlich erfolgte Einführung von Erweiterungsmethoden reduziert, mit denen Implementierungen für Schnittstellenmethoden bereitgestellt werden können. Ein weiterer Unterscheidungsfaktor besteht darin, dass eine Klasse nur eine abstrakte Klasse erben kann (d. H. Es gibt keine Mehrfachvererbung), aber mehrere Schnittstellen implementieren kann. 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 Abfragefunktionen für jeden Typ bereitgestellt werden, der IEnumerable über eine Vielzahl von Erweiterungsmethoden implementiert.

70
Gulshan

Wenn Sie einen Teil der Klasse benötigen, der implementiert werden soll. Das beste Beispiel, das ich verwendet habe, ist das Muster Vorlagenmethode .

public abstract class SomethingDoer
{
    public void Do()
    {
        this.DoThis();
        this.DoThat();
    }

    protected abstract void DoThis();
    protected abstract void DoThat();
}

Auf diese Weise können Sie die Schritte definieren, die beim Aufruf von Do () ausgeführt werden, ohne zu wissen, wie sie implementiert werden. Das Ableiten von Klassen muss die abstrakten Methoden implementieren, nicht jedoch die Do () -Methode.

Erweiterungsmethoden erfüllen nicht unbedingt den Teil "muss ein Teil der Klasse sein" der Gleichung. Darüber hinaus können (anscheinend) iirc-Erweiterungsmethoden nichts anderes als öffentlich sein.

edit

Die Frage ist interessanter, als ich ursprünglich angenommen hatte. Bei weiterer Prüfung stellte Jon Skeet beantwortet eine Frage wie diese auf SO zugunsten der Verwendung von Schnittstellen + Erweiterungsmethoden. Auch ein möglicher Nachteil verwendet Reflexion gegen eine auf diese Weise entworfene Objekthierarchie.

Persönlich habe ich Probleme, den Vorteil einer Änderung einer derzeit gängigen Praxis zu erkennen, sehe aber auch nur wenige bis gar keine Nachteile.

Es ist zu beachten, dass es möglich ist, auf diese Weise in vielen Sprachen über Utility-Klassen zu programmieren. Erweiterungen liefern nur den syntaktischen Zucker, damit die Methoden so aussehen, als ob sie zur Klasse gehören.

63
Steven Evers

Es geht nur um was Sie möchten modellieren:

Abstrakte Klassen arbeiten mit Vererbung. Als spezielle Basisklassen modellieren sie eine is-a - Beziehung.

Zum Beispiel ein Hund ist ein Tier, also haben wir

class Dog : Animal { ... }

Da es keinen Sinn macht, create ein generisches Alltier zu sein (wie würde es aussehen?), Machen wir diese Animal Basisklasse abstract - aber es ist immer noch eine Basisklasse. Und die Dog Klasse macht keinen Sinn, ohne ein Animal zu sein.

Interfaces dagegen ist eine andere Geschichte. Sie verwenden keine Vererbung, sondern bieten Polymorphismus (was kann auch mit Vererbung implementiert werden kann). Sie modellieren keine is-a Beziehung, sondern eher eine es unterstützt.

Nehmen Sie z.B. IComparable - ein Objekt, das den Vergleich mit einem anderen unterstützt.

Die Funktionalität einer Klasse hängt nicht ab von den von ihr implementierten Schnittstellen. Die Schnittstelle bietet lediglich eine allgemeine Möglichkeit, auf die Funktionalität zuzugreifen. Wir könnten immer noch Dispose eines Graphics oder eines FileStream, ohne dass sie IDisposable implementieren. Die Schnittstelle bezieht sich nur die Methoden zusammen.

Im Prinzip könnte man Schnittstellen wie Erweiterungen hinzufügen und entfernen, ohne das Verhalten einer Klasse zu ändern und nur den Zugriff darauf zu bereichern. Sie können jedoch keine Basisklasse entfernen, da das Objekt bedeutungslos werden würde!

25
Dario

Ich habe gerade festgestellt, dass ich seit Jahren keine abstrakten Klassen mehr verwendet habe (zumindest nicht erstellt).

Die abstrakte Klasse ist ein etwas seltsames Tier 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 "offenlegen" (oder irgendwelche Bindungen herstellen).

Selbst wenn zwischen Klassen, die eine Schnittstelle implementieren, ein gemeinsames Verhalten erforderlich ist, würde ich eine unabhängige Hilfsklasse anstelle einer abstrakten Klasse verwenden.

Ich würde für Schnittstellen gehen.

3
Maglob

IMHO, ich denke, dass es hier eine Mischung von Konzepten gibt.

Klassen verwenden eine bestimmte Art der Vererbung, während Schnittstellen eine andere Art der 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 Schnittstellen beginnt vor langer Zeit 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 von Klassen verwendete Art der Vererbung, die als Implementierungsvererbung bezeichnet wird.

Um die GoF Book-Empfehlung anwenden zu können: "Zum Programmieren für die Schnittstelle und nicht für die Implementierung", unterstützte C++ abstrakte Klassen und die Vererbung mehrerer Implementierungen, um das tun zu können, was Schnittstellen in C # können (und nützlich!) .

C # führt eine neue Art von Typ, Schnittstellen und eine neue Art von Vererbung ein, die Schnittstellenvererbung, um mindestens zwei verschiedene Möglichkeiten zur Unterstützung von "Programmieren für die Schnittstelle und nicht für die Implementierung" mit einer wichtigen Einschränkung anzubieten: Nur C # unterstützt Mehrfachvererbung mit Schnittstellenvererbung (und nicht mit Implementierungsvererbung).

Die architektonischen Gründe für Schnittstellen in C # sind die GoF-Empfehlungen.

Ich hoffe das kann hilfreich sein.

Herzliche Grüße, Gastón

Das Anwenden von Erweiterungsmethoden auf eine Schnittstelle ist nützlich, um allgemeines Verhalten auf Klassen anzuwenden, die möglicherweise nur eine gemeinsame Schnittstelle verwenden. Solche Klassen können bereits existieren und auf andere Weise für Erweiterungen geschlossen werden.

Für neue Designs würde ich die Verwendung von Erweiterungsmethoden für Schnittstellen bevorzugen. Für eine 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 oben in einer Hierarchie die private Implementierung hinter einer öffentlichen Schnittstelle kapseln muss, ist eine abstrakte Klasse der einzige Weg.

1
Ed James

Ich denke, dass in C # die Mehrfachvererbung nicht unterstützt wird, und deshalb wird das Konzept der Schnittstelle in C # eingeführt.

Bei abstrakten Klassen wird auch die Mehrfachvererbung nicht unterstützt. Es ist also einfach zu entscheiden, ob abstrakte Klassen oder Schnittstellen verwendet werden sollen.

0
Beginner