Ich habe einige Probleme mit der Vererbung, da ich eine Gruppe von zusammenhängenden abstrakten Klassen habe, die alle zusammen überschrieben werden müssen, um eine Client-Implementierung zu erstellen. Idealerweise würde ich gerne Folgendes machen:
%Vor%Dies würde es jedem ermöglichen, die Dog-Klasse zu benutzen, um DogLegs und jeden, der die Animal-Klasse benutzt, automatisch Legs zu bekommen. Das Problem besteht darin, dass die überschriebene Funktion den gleichen Typ wie die Basisklasse haben muss, damit diese nicht kompiliert wird. Ich verstehe nicht, warum das nicht so sein sollte, denn DogLeg ist implizit auf Leg legal. Ich weiß, dass es viele Möglichkeiten gibt, aber ich bin neugierig, warum dies in C # nicht möglich ist.
BEARBEITEN : Ich habe das etwas modifiziert, da ich eigentlich Eigenschaften anstelle von Funktionen in meinem Code verwende.
BEARBEITEN : Ich habe es wieder in Funktionen geändert, weil die Antwort nur auf diese Situation zutrifft (Kovarianz auf dem Wert-Parameter der Set-Funktion einer Eigenschaft sollte nicht funktionieren) . Entschuldigung für die Schwankungen! Mir ist klar, dass viele Antworten irrelevant erscheinen.
Die kurze Antwort ist, dass GetLeg in seinem Rückgabetyp invariant ist. Die lange Antwort finden Sie hier: Kovarianz und Kontravarianz
Ich möchte hinzufügen, dass die Vererbung zwar das erste Abstraktionswerkzeug ist, das die meisten Entwickler aus ihrer Toolbox ziehen, es aber fast immer möglich ist, Komposition zu verwenden. Komposition ist etwas mehr Arbeit für den API-Entwickler, macht aber die API für ihre Konsumenten nützlicher.
Hund sollte als Rückgabetyp ein Bein und kein Hundeleg zurückgeben. Die tatsächliche Klasse kann ein DogLeg sein, aber der Punkt ist zu entkoppeln, damit der Benutzer von Dog nicht über DogLegs wissen muss, sie müssen nur über Beine wissen.
Ändern:
%Vor%zu:
%Vor%Tu das nicht:
%Vor%es vereitelt den Zweck der Programmierung für den abstrakten Typ.
Der Grund, DogLeg zu verbergen, ist, weil die GetLeg-Funktion in der abstrakten Klasse ein Abstraktes Bein zurückgibt. Wenn Sie das GetLeg außer Kraft setzen, müssen Sie ein Leg zurückgeben. Das ist der Punkt, eine Methode in einer abstrakten Klasse zu haben. Um diese Methode zu ihren Kindern zu verbreiten. Wenn Sie möchten, dass die Benutzer des Hundes über DogLegs Bescheid wissen, machen Sie eine Methode namens GetDogLeg und geben Sie ein DogLeg zurück.
Wenn Sie tun könnten, was der Fragesteller möchte, dann müsste jeder Benutzer von Animal über ALLE Tiere Bescheid wissen.
Es ist ein vollkommen gültiger Wunsch, dass die Signatur eine überschreibende Methode einen Rückgabetyp hat, der ein Untertyp des Rückgabetyps in der überschriebenen Methode ist ( puh ). Immerhin sind sie laufzeittypkompatibel.
Aber C # unterstützt noch nicht "kovariante Rückgabetypen" in überschriebenen Methoden (anders als C ++ [1998] & amp; Java [2004]).
Sie müssen umgehen und auf absehbare Zeit auskommen, wie Eric Lippert in sein Blog [19. Juni 2008]:
Diese Art von Varianz wird "Kovarianz vom Rückgabetyp" genannt.
Wir haben nicht vor, diese Art von Varianz in C # zu implementieren.
Tun Sie es so, dann können Sie die Abstraktion in Ihrem Klienten benutzen:
%Vor%Dann können Sie es bei Bedarf umsetzen:
%Vor%Völlig erfunden, aber der Punkt ist, dass Sie das tun können:
%Vor%Sie haben zwar weiterhin die Möglichkeit, eine spezielle Buck-Methode für Doglegs zu verwenden.
Sie können Generics und Schnittstellen verwenden, um das in C # zu implementieren:
%Vor%Vielleicht ist es einfacher, das Problem anhand eines Beispiels zu sehen:
%Vor%Nun, das sollte kompilieren, wenn Sie Dog kompiliert sind, aber wir wollen wahrscheinlich keine solche Mutante.
Ein verwandtes Problem ist, sollte Dog [] ein Animal [] sein, oder IList & lt; Dog & gt; ein IList & lt; Animal & gt;?
C # verfügt über explizite Schnittstellenimplementierungen, um genau dieses Problem zu beheben:
%Vor%Wenn Sie einen Hund über eine Referenz vom Typ Hund haben, gibt der Aufruf von GetLeg () ein DogLeg zurück. Wenn Sie dasselbe Objekt haben, aber der Verweis vom Typ IAnimal ist, wird ein Bein zurückgegeben.
Sie können auch die Schnittstelle ILeg zurückgeben, die beide Leg und / oder DogLeg implementieren.
Wichtig ist, dass Sie an jeder Stelle, an der Sie den Basistyp verwenden, einen abgeleiteten Typ verwenden können (Sie können Dog an jede Methode / Eigenschaft / Feld / Variable übergeben, die Animal erwartet)
Nehmen wir diese Funktion:
%Vor%Eine vollkommen gültige Funktion, jetzt nennen wir die Funktion so:
%Vor%Wenn die Eigenschaft Dog.Leg nicht vom Typ Leg ist, enthält die AddLeg-Funktion plötzlich einen Fehler und kann nicht kompiliert werden.
@Luke
Ich denke Ihre vielleicht missverstandene Erbschaft. Dog.GetLeg () wird ein DogLeg-Objekt zurückgeben.
%Vor%Die aufgerufene Methode heißt Dog.GetLeg (); und DogLeg.Kick () (ich nehme eine Methode an, für die Leg.kick () existiert), da der deklarierte Rückgabetyp DogLeg unnötig ist, weil das zurückgegeben wird, auch wenn der Rückgabetyp für Dog.GetLeg () ist Bein.
Sie können erreichen, was Sie wollen, indem Sie ein generisches Element mit einer geeigneten Einschränkung wie folgt verwenden:
%Vor%Tags und Links c# inheritance covariance oop contravariance