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?
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.
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
ist sicher, da ein String-Argument immer gültig ist, wenn ein Objekt-Argument ist.
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.
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%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.
Wenn T
sowieso auf eine bestimmte Schnittstelle beschränkt ist, können Sie stattdessen nur diese Schnittstelle verwenden:
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.
Tags und Links .net c# generics covariance contravariance