Eine generische Methode kann kontravariante / kovariante Typen verwenden?

8

Ich schreibe eine verallgemeinerte Methode, um es in einer speziellen Aufgabe in einer T4-Vorlage zu verwenden. Die Methode sollte mir ermöglichen, spezialisierte Typen von einer allgemeinen Schnittstelle zu verwenden. Ich dachte über die folgenden Unterschriften nach:

%Vor%

Wenn ich versuche, IGreatInterface zu implementieren, kennzeichnet der Compiler einen Fehler für aMethodBeta() , weil ich meinen T4 dazu gebracht habe, diese Methode mit einem Untertyp von IAnInterface zu schreiben (dh ich möchte diese Methode folgendermaßen implementieren: Object aMethodBeta(AnInterestingClass parameter) ).

Methode aMethodAlpha<U>() kann verwendet werden, ist aber nicht so sauber wie ich es möchte, da mein T4 zusätzlichen Code generieren muss. Ich (vielleicht zu Unrecht) schlagen vor, dass eine Implementierung dieser Methode, die von einem T4 durchgeführt werden muss, in Object aMethodAlpha<AnInterestingClass>(AnInterestingClass parameter) erfolgen kann.

Ich denke, dass generische Methoden kontravariante Typen nicht unterstützen, aber ich bin mir nicht sicher; Ich nehme an, dass es die Art ist, wie der Compiler den Coder daran hindert, einen bestimmten Typ mit einer Methode zu verwenden, die nicht im allgemeinen Typ definiert ist ...

  1. Muss eine generische Methode den exakten Typ bei der Implementierung verwenden?
  2. Gibt es einen Trick, um dieses Verhalten zu ändern?
JPCF 11.11.2011, 20:54
quelle

3 Antworten

20

Diese Frage ist ziemlich verwirrend. Lass mich sehen, ob ich es klären kann.

  

Wenn ich versuche, IGreatInterface zu implementieren, markiert der Compiler einen Fehler für aMethodBeta() , weil ich diese Methode mit einem Untertyp IAnInterface erstellt habe. Ich möchte diese Methode folgendermaßen implementieren: Object aMethodBeta(AnInterestingClass parameter) .

Das ist nicht legal. Etwas vereinfacht:

%Vor%

Die Klasse Vegetarian erfüllt nicht den Vertrag von IEater . Du solltest any Essen zu essen weitergeben können, aber ein Vegetarian akzeptiert nur Obst. C # unterstützt die formale Parameterkovarianz der virtuellen Methode nicht, da diese nicht typsicher ist.

Nun könnten Sie sagen, wie wäre es damit:

%Vor%

Jetzt haben wir Typ Sicherheit; Omnivore kann als IFruitEater verwendet werden, weil ein Omnivore sowohl Früchte als auch andere Nahrungsmittel essen kann.

Leider unterstützt C # nicht den formalen Parametertyp convaricance der virtuellen Methode , obwohl dies theoretisch typsicher ist. Nur wenige Sprachen unterstützen dies.

In ähnlicher Weise unterstützt C # auch die virtuelle Methodenrückgabetypvarianz nicht.

Ich bin mir nicht sicher, ob das tatsächlich deine Frage beantwortet hat oder nicht. Kannst du die Frage klären?

UPDATE:

Was ist mit:

%Vor%

Nein, das ist auch nicht legal. Der Vertrag von IEater besteht darin, dass Sie eine Methode Eat<T> bereitstellen, die jede T übernehmen kann, die ein Food ist. Sie können den Vertrag nicht teilweise implementieren, genauso wenig wie Sie dies tun könnten:

%Vor%

Sie können dies jedoch tun:

%Vor%

Das ist vollkommen legal. Sie können jedoch nicht:

%Vor%

Da C # wiederum keine formale Parameterkomparvarianz oder Kovarianz der virtuellen Methode unterstützt.

Beachten Sie, dass C # parametrische Polymorphismuskovarianz unterstützt, wenn dies als typsicher bekannt ist. Zum Beispiel ist dies legal:

%Vor%

Eine Folge von Früchten kann als eine Sequenz von Lebensmitteln verwendet werden. Oder,

%Vor%

Wenn Sie etwas haben, das irgendwelche zwei Früchte vergleichen kann, dann kann es irgendwelche zwei Äpfel vergleichen.

Diese Art von Kovarianz und Kontravarianz ist jedoch nur dann zulässig, wenn alle folgenden Aussagen zutreffen: (1) die Varianz ist nachweisbar typsicher, (2) der Autor des Typs hat Varianzanmerkungen hinzugefügt, die die gewünschte Co- und Kontra-Varianz angeben. Varianzen, (3) die verschiedenen Typargumente sind alle Referenztypen, (4) der generische Typ ist entweder ein Delegat oder eine Schnittstelle.

    
Eric Lippert 11.11.2011, 22:06
quelle
2

Wenn Sie von einer generischen Schnittstelle erben möchten, sehen Sie sich die Antwort von phoog an. Wenn Sie darüber reden, eine Schnittstelle co-variant zu implementieren, führt das zu meiner Diskussion.

Angenommen:

%Vor%

Das Problem mit dem Versuch, die Schnittstelle mit einem abgeleiteten (co-varianten) Argument zu implementieren, besteht darin, dass es keine Garantie gibt, wenn dies über eine Schnittstelle aufgerufen wird, die eine IAnInterface Instanz als SomeSubClass Instanz darstellt. Deshalb ist es nicht direkt erlaubt.

%Vor%

IF Sie könnten Kovarianz machen, dies würde fehlschlagen, weil Sie ein SomeSubClass erwarten würden, aber ein AnotherSubClass erhalten würde.

Was Sie können tun, ist eine explizite Schnittstellenimplementierung:

%Vor%

Wenn Sie dies getan haben, unterstützt Ihre Schnittstelle das generische, die Klasse hat eine spezifischere Methode, unterstützt aber trotzdem die Schnittstelle. Der Hauptunterschied besteht darin, dass Sie den Fall behandeln müssen, in dem eine unerwartete Implementierung von IAnInterface übergeben wird.

AKTUALISIEREN : Es klingt so, als wolltest du so etwas:

%Vor%

Dies ist nicht erlaubt, wenn Sie eine generische Methode von einer Schnittstelle implementieren, müssen die Bedingungen übereinstimmen.

    
James Michael Hare 11.11.2011 21:08
quelle
0

Vielleicht suchen Sie das:

%Vor%

BEARBEITEN:

Ja, Sie haben Recht: Wenn Sie eine generische Methode in einer Schnittstelle definieren, können Sie diese Methode nicht mit einer konkreten Methode implementieren, die einen kompatiblen Typ verwendet.

Wie wäre es mit einem Delegaten (da Delegierte Ko- und Kontravarianz unterstützen):

[Beispiel gelöscht, weil ich die Abweichung rückwärts bekommen habe - es funktioniert nicht.]

    
phoog 11.11.2011 21:20
quelle

Tags und Links