Generische Einschränkung für Aktion funktioniert nicht wie erwartet

8

Ich habe Probleme zu verstehen, warum das folgende Snippet mir keinen Fehler gibt

%Vor%

Aber dieser, den ich wegen der generischen Typbeschränkung erwarten würde

%Vor%

Gibt diesen Fehler

%Vor%

Ich verwende VS2012 sp1 und .NET 4.5.

Kann jemand erklären, warum die Einschränkung dies nicht zu kompilieren erlaubt?

    
Jim Jeffries 25.03.2013, 08:55
quelle

6 Antworten

3

Klassen und Delegierte sind nicht dasselbe. System.Action<MyInterface> repräsentiert eine Funktion mit einem einzelnen Parameter vom Typ MyInterface , während System.Action<T> eine Methode mit einem Parameter vom Typ T : MyInterface darstellt. Die Funktionssignaturen sind nicht kompatibel, es ist nicht relevant, dass T eine Ableitung von MyInterface ist, die Signatur wäre nur kompatibel, wenn T genau MyInterface wäre.

    
odyss-jii 25.03.2013, 09:12
quelle
4

Dies ist ein Kontravarisierungsproblem - ein Action<MyInterface> sollte jede MyInterface -Instanz als Argument verwenden können, aber Sie versuchen, ein Action<T> zu speichern, wobei T ein Untertyp von MyInterface ist, der ist nicht sicher.

Zum Beispiel, wenn Sie hatten:

%Vor%

Sie können% Action<T> nur dann einem Action<U> zuweisen, wenn der Typ T 'kleiner' ist als der Typ U . Zum Beispiel

%Vor%

ist sicher, da ein String-Argument immer gültig ist, wenn ein Objekt-Argument ist.

    
Lee 25.03.2013 09:12
quelle
2

Ich finde es hilfreich in diesen Situationen zu überlegen, was schief läuft, wenn Sie das Verhalten erlauben. Also lasst uns darüber nachdenken.

%Vor%

Was passiert also? Wir erstellen einen Delegaten, der einen Tiger nimmt und aufgibt, ihn an Subscribe<Tiger> weitergibt, ihn in Action<IAnimal> umwandelt und eine Giraffe gibt, die dann auftaucht.

Offensichtlich muss das illegal sein. Der einzige Ort, wo es sinnvoll ist, es illegal zu machen, ist die Konvertierung von Action<Tiger> nach Action<IAnimal> . Das ist also illegal.

    
Eric Lippert 25.03.2013 14:52
quelle
1

Die where T: MyInterface Einschränkung bedeutet " any Instanz der any Klasse oder Struktur, die MyInterface implementiert".

Sie können also Folgendes vereinfachen:

%Vor%

Was nicht funktionieren soll, während noch IList : IEnumerable . Weitere Details finden Sie hier:

Ссылка Ссылка

Wenn Sie also wirklich eine generische und nicht nur eine Schnittstelle verwenden müssen, können Sie das so tun, obwohl es Komplexität und kleinere Leistungsprobleme hinzufügt:

%Vor%     
Lanorkin 25.03.2013 09:19
quelle
1

Klassen und Delegierte verhalten sich ein wenig anders. Lassen Sie uns ein einfaches Beispiel sehen:

%Vor%

Bei dieser Methode könnten Sie annehmen, dass T mindestens MyInterface wäre, also könnten Sie etwas wie MyInterface e = arg; tun, weil Argumente immer in MyInterface umgewandelt werden könnten.

Sehen wir uns nun an, wie sich die Delegierten verhalten:

%Vor%

Jetzt fügen wir der CallAdresse DerivedClass myActionList hinzu und rufen dann irgendwo Delegate auf:

%Vor%

Aber Sie können das nicht tun, denn wenn Sie den Callback von DerivedClass haben, müssen Sie ihm DerivedClass als Parameter übergeben.

Diese Frage bezieht sich auf Kovarianz und Kontravarianz . Sie können über die Abweichung von diesem Artikel lesen Auch Eric Lippert hat sehr interessante Artikel über Varianz, das ist der erste Artikel, den Rest findest du in seinem Blog.

P.S. Bearbeitet nach Lee Kommentar.

    
Andrew 25.03.2013 09:39
quelle
0

Wenn T sowieso auf eine bestimmte Schnittstelle beschränkt ist, können Sie stattdessen nur diese Schnittstelle verwenden:

%Vor%

Wird funktionieren und kompilieren und ist praktisch das gleiche wie das, was Sie jetzt haben.

Generics sind nützlich, wenn Sie die gleiche Operation unabhängig vom Typ ausführen möchten, wenn Sie dann den Typ auf eine Schnittstelle beschränken, haben Sie den Zweck von Generika besiegt und sollten stattdessen nur diese Schnittstelle verwenden.

    
Bazzz 25.03.2013 09:10
quelle