Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Kovarianz und Kontravarianz sind Begriffe, die auf die Möglichkeit verweisen, einen abgeleiteten Typ (spezifischer) oder einen weniger abgeleiteten Typ (weniger spezifisch) als ursprünglich angegeben zu verwenden. Generische Typparameter unterstützen Kovarianz und Kontravarianz, um eine größere Flexibilität bei der Zuweisung und Verwendung generischer Typen zu bieten.
Wenn Sie auf ein Typsystem verweisen, weisen Kovarianz, Kontravarianz und Invarianz die folgenden Definitionen auf. In den Beispielen wird eine Basisklasse mit dem Namen Base
und eine abgeleitete Klasse mit dem Namen Derived
angenommen.
Covariance
Ermöglicht Ihnen die Verwendung eines weiter abgeleiteten Typs als ursprünglich angegeben.
Sie können einer Variablen vom Typ
IEnumerable<Derived>
eine InstanzIEnumerable<Base>
zuweisen.Contravariance
Ermöglicht die Verwendung eines generischeren (weniger abgeleiteten) Typs als ursprünglich angegeben.
Sie können einer Variablen vom Typ
Action<Base>
eine InstanzAction<Derived>
zuweisen.Invariance
Bedeutet, dass Sie nur den ursprünglich angegebenen Typ verwenden können. Ein invarianter generischer Typparameter ist weder kovariant noch kontravariant.
Sie können einer Variablen vom Typ
List<Base>
keine Instanz vonList<Derived>
zuweisen oder umgekehrt.
Kovariante Typparameter ermöglichen es Ihnen, Zuordnungen vorzunehmen, die ähnlich wie gewöhnliche Polymorphismen aussehen, wie im folgenden Code gezeigt.
IEnumerable<Derived> d = new List<Derived>();
IEnumerable<Base> b = d;
Dim d As IEnumerable(Of Derived) = New List(Of Derived)
Dim b As IEnumerable(Of Base) = d
Die List<T> -Klasse implementiert die generische IEnumerable<T> -Schnittstelle. List<Derived>
(List(Of Derived)
in Visual Basic) implementiert daher IEnumerable<Derived>
. Der kovariante Typparameter führt den Rest aus.
Kontravarianz hingegen scheint kontraintuitiv zu sein. Im folgenden Beispiel wird ein Delegat vom Typ Action<Base>
(Action(Of Base)
in Visual Basic) erstellt und anschließend einer Variablen vom Typ Action<Derived>
zugewiesen.
Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };
Action<Derived> d = b;
d(new Derived());
Dim b As Action(Of Base) = Sub(target As Base)
Console.WriteLine(target.GetType().Name)
End Sub
Dim d As Action(Of Derived) = b
d(New Derived())
Dies scheint rückwärts zu sein, aber es ist typsicherer Code, der kompiliert und ausgeführt wird. Der Lambda-Ausdruck stimmt mit dem Delegat überein, dem er zugewiesen ist. Daher wird eine Methode definiert, die einen Parameter vom Typ Base
verwendet und keinen Rückgabewert aufweist. Der resultierende Delegat kann einer Variablen vom Typ Action<Derived>
zugewiesen werden, da der Typparameter T
des Action<T> Delegaten kontravariant ist. Der Code ist typsicher, da T
er einen Parametertyp angibt. Wenn der Delegat des Typs Action<Base>
aufgerufen wird, als ob es sich um einen Delegat vom Typ Action<Derived>
handelt, muss das Argument vom Typ Derived
sein. Dieses Argument kann immer sicher an die zugrunde liegende Methode übergeben werden, da der Parameter der Methode vom Typ Base
ist.
Im Allgemeinen lässt sich ein kovarianter Typparameter als Rückgabetyp eines Delegierten verwenden, während kontravariante Typparameter als Parametertypen dienen können. Für eine Schnittstelle können kovariante Typparameter als Rückgabetypen der Methoden der Schnittstelle verwendet werden, und kontravariante Typparameter können als Parametertypen der Methoden der Schnittstelle verwendet werden.
Kovarianz und Kontravarianz werden gemeinsam als Varianz bezeichnet. Ein generischer Typparameter, der nicht als kovariant oder kontravariant gekennzeichnet ist, wird als invariant bezeichnet. Eine kurze Zusammenfassung der Fakten über Unterschiede in der Common Language Runtime.
Variant-Typparameter sind auf generische Schnittstellen- und generische Delegattypen beschränkt.
Eine generische Schnittstelle oder ein generischer Delegattyp kann sowohl über kovariante als auch über kontravariante Typparameter verfügen.
Die Varianz gilt nur für Bezugstypen; Wenn Sie einen Werttyp für einen Variant-Typ-Parameter angeben, ist dieser Typparameter für den resultierenden konstruierten Typ invariant.
Varianz gilt nicht für eine Delegatkombination. Das heißt, bei zwei Delegaten von Typen
Action<Derived>
undAction<Base>
(Action(Of Derived)
undAction(Of Base)
in Visual Basic) können Sie den zweiten Delegat nicht mit dem ersten kombinieren, obwohl das Ergebnis typsicher wäre. Bei Varianz kann der zweite Delegat einer Variable desAction<Derived>
-Typs zugewiesen werden, Delegaten können aber nur kombiniert werden, wenn ihre Typen genau überstimmen.Ab C# 9 werden kovariante Rückgabetypen unterstützt. Eine überschreibende Methode kann einen stärker abgeleiteten Rückgabetyp deklarieren als die Methode, die überschrieben wird, und eine überschreibende schreibgeschützte Eigenschaft kann einen stärker abgeleiteten Typ deklarieren.
Generische Schnittstellen mit kovarianten Typparametern
Mehrere generische Schnittstellen weisen kovariante Typparameter auf, z. B. , IEnumerable<T>, IEnumerator<T>, IQueryable<T>und IGrouping<TKey,TElement>. Alle Typparameter dieser Schnittstellen sind kovariant, sodass sie nur für die Rückgabetypen der Member verwendet werden.
Im folgenden Beispiel werden kovariante Typparameter veranschaulicht. Im Beispiel werden zwei Typen definiert: Base
hat eine statische Methode mit dem Namen PrintBases
, die ein IEnumerable<Base>
(IEnumerable(Of Base)
in Visual Basic) übernimmt und die Elemente ausgibt.
Derived
erbt von Base
. Im Beispiel wird ein leeres List<Derived>
(List(Of Derived)
in Visual Basic) erstellt und es wird veranschaulicht, dass dieser Typ an PrintBases
übergeben und ohne Umwandlung einer Variablen vom Typ IEnumerable<Base>
zugewiesen werden kann.
List<T> implementiert IEnumerable<T>, das einen einzigen kovarianten Typparameter hat. Der kovariante Typparameter ist der Grund, warum eine Instanz von IEnumerable<Derived>
anstelle von IEnumerable<Base>
verwendet werden kann.
using System;
using System.Collections.Generic;
class Base
{
public static void PrintBases(IEnumerable<Base> bases)
{
foreach(Base b in bases)
{
Console.WriteLine(b);
}
}
}
class Derived : Base
{
public static void Main()
{
List<Derived> dlist = new List<Derived>();
Derived.PrintBases(dlist);
IEnumerable<Base> bIEnum = dlist;
}
}
Imports System.Collections.Generic
Class Base
Public Shared Sub PrintBases(ByVal bases As IEnumerable(Of Base))
For Each b As Base In bases
Console.WriteLine(b)
Next
End Sub
End Class
Class Derived
Inherits Base
Shared Sub Main()
Dim dlist As New List(Of Derived)()
Derived.PrintBases(dlist)
Dim bIEnum As IEnumerable(Of Base) = dlist
End Sub
End Class
Generische Schnittstellen mit kontravarianten Typparametern
Mehrere generische Schnittstellen weisen kontravariante Typparameter auf; Beispiel: IComparer<T>, , IComparable<T>und IEqualityComparer<T>. Diese Schnittstellen weisen nur kontravariante Typparameter auf, sodass die Typparameter nur als Parametertypen in den Membern der Schnittstellen verwendet werden.
Im folgenden Beispiel werden kontravariante Typparameter veranschaulicht. Im Beispiel wird eine abstrakte Klasse (MustInherit
in Visual Basic) Shape
mit einer Area
Eigenschaft definiert. Das Beispiel definiert auch eine ShapeAreaComparer
Klasse, die implementiert IComparer<Shape>
(IComparer(Of Shape)
in Visual Basic). Die Implementierung der IComparer<T>.Compare Methode basiert auf dem Wert der Area
Eigenschaft, sodass ShapeAreaComparer
Objekte nach Fläche Shape
sortiert werden können.
Die Circle
Klasse erbt Shape
und überschreibt Area
. Im Beispiel wird ein SortedSet<T> von Circle
-Objekten erstellt, wobei ein Konstruktor verwendet wird, der IComparer<Circle>
(IComparer(Of Circle)
in Visual Basic) akzeptiert. Anstatt jedoch ein IComparer<Circle>
Objekt zu übergeben, übergibt das Beispiel ein ShapeAreaComparer
Objekt, welches IComparer<Shape>
implementiert. Das Beispiel kann einen Vergleichsoperator eines weniger abgeleiteten Typs (Shape
) übergeben, wenn der Code einen Vergleichsoperator eines stärker abgeleiteten Typs erfordert (Circle
), da der Typparameter der IComparer<T> generischen Schnittstelle kontravariant ist.
Jedes Mal, wenn ein neues Circle
Objekt dem SortedSet<Circle>
hinzugefügt wird und mit einem vorhandenen Element verglichen wird, wird die IComparer<Shape>.Compare
Methode (IComparer(Of Shape).Compare
Methode in Visual Basic) des ShapeAreaComparer
Objekts aufgerufen. Der Parametertyp der Methode (Shape
) ist kleiner als der Typ, der übergeben wird (Circle
), sodass der Aufruf sicher ist. Kontravarianz ermöglicht es ShapeAreaComparer
, eine Auflistung eines einzelnen Typs sowie eine Auflistung von gemischten Typen zu sortieren, die von Shape
abgeleitet werden.
using System;
using System.Collections.Generic;
abstract class Shape
{
public virtual double Area { get { return 0; }}
}
class Circle : Shape
{
private double r;
public Circle(double radius) { r = radius; }
public double Radius { get { return r; }}
public override double Area { get { return Math.PI * r * r; }}
}
class ShapeAreaComparer : System.Collections.Generic.IComparer<Shape>
{
int IComparer<Shape>.Compare(Shape a, Shape b)
{
if (a == null) return b == null ? 0 : -1;
return b == null ? 1 : a.Area.CompareTo(b.Area);
}
}
class Program
{
static void Main()
{
// You can pass ShapeAreaComparer, which implements IComparer<Shape>,
// even though the constructor for SortedSet<Circle> expects
// IComparer<Circle>, because type parameter T of IComparer<T> is
// contravariant.
SortedSet<Circle> circlesByArea =
new SortedSet<Circle>(new ShapeAreaComparer())
{ new Circle(7.2), new Circle(100), null, new Circle(.01) };
foreach (Circle c in circlesByArea)
{
Console.WriteLine(c == null ? "null" : "Circle with area " + c.Area);
}
}
}
/* This code example produces the following output:
null
Circle with area 0.000314159265358979
Circle with area 162.860163162095
Circle with area 31415.9265358979
*/
Imports System.Collections.Generic
MustInherit Class Shape
Public MustOverride ReadOnly Property Area As Double
End Class
Class Circle
Inherits Shape
Private r As Double
Public Sub New(ByVal radius As Double)
r = radius
End Sub
Public ReadOnly Property Radius As Double
Get
Return r
End Get
End Property
Public Overrides ReadOnly Property Area As Double
Get
Return Math.Pi * r * r
End Get
End Property
End Class
Class ShapeAreaComparer
Implements System.Collections.Generic.IComparer(Of Shape)
Private Function AreaComparer(ByVal a As Shape, ByVal b As Shape) As Integer _
Implements System.Collections.Generic.IComparer(Of Shape).Compare
If a Is Nothing Then Return If(b Is Nothing, 0, -1)
Return If(b Is Nothing, 1, a.Area.CompareTo(b.Area))
End Function
End Class
Class Program
Shared Sub Main()
' You can pass ShapeAreaComparer, which implements IComparer(Of Shape),
' even though the constructor for SortedSet(Of Circle) expects
' IComparer(Of Circle), because type parameter T of IComparer(Of T)
' is contravariant.
Dim circlesByArea As New SortedSet(Of Circle)(New ShapeAreaComparer()) _
From {New Circle(7.2), New Circle(100), Nothing, New Circle(.01)}
For Each c As Circle In circlesByArea
Console.WriteLine(If(c Is Nothing, "Nothing", "Circle with area " & c.Area))
Next
End Sub
End Class
' This code example produces the following output:
'
'Nothing
'Circle with area 0.000314159265358979
'Circle with area 162.860163162095
'Circle with area 31415.9265358979
Generische Delegaten mit Variantentypparametern
Die Func
generischen Delegaten, wie Func<T,TResult>, haben kovariante Rückgabetypen und kontravariante Parametertypen. Die Action
generischen Delegaten, wie Action<T1,T2>, weisen kontravariante Parametertypen auf. Dies bedeutet, dass den Stellvertretungen Variablen zugewiesen werden können, die mehr abgeleitete Parametertypen haben und (im Fall der Func
generischen Stellvertretungen) weniger abgeleitete Rückgabetypen.
Hinweis
Der letzte generische Typparameter der generischen Func
-Delegaten gibt den Typ des Rückgabewerts in der Signatur des Delegaten an. Es ist kovariant (out
Schlüsselwort), während die anderen generischen Typparameter kontravariant (in
Schlüsselwort) sind.
Der folgende Code veranschaulicht dies. Der erste Codeabschnitt definiert eine Klasse namens Base
, eine Klasse mit dem Namen Derived
, die erbt Base
, und eine andere Klasse mit einer static
Methode (Shared
in Visual Basic) mit dem Namen MyMethod
. Die Methode nimmt eine Instanz von Base
und gibt eine Instanz von Derived
zurück. (Wenn es sich bei dem Argument um eine Instanz von Derived
, MyMethod
gibt sie zurück; wenn das Argument eine Instanz von Base
ist , MyMethod
gibt eine neue Instanz von Derived
.) In Main()
, erstellt das Beispiel eine Instanz von Func<Base, Derived>
(Func(Of Base, Derived)
in Visual Basic), die darstellt MyMethod
, und speichert sie in der Variablen f1
.
public class Base {}
public class Derived : Base {}
public class Program
{
public static Derived MyMethod(Base b)
{
return b as Derived ?? new Derived();
}
static void Main()
{
Func<Base, Derived> f1 = MyMethod;
Public Class Base
End Class
Public Class Derived
Inherits Base
End Class
Public Class Program
Public Shared Function MyMethod(ByVal b As Base) As Derived
Return If(TypeOf b Is Derived, b, New Derived())
End Function
Shared Sub Main()
Dim f1 As Func(Of Base, Derived) = AddressOf MyMethod
Der zweite Codeabschnitt zeigt, dass der Delegat einer Variablen vom Typ Func<Base, Base>
(Func(Of Base, Base)
in Visual Basic) zugewiesen werden kann, da der Rückgabetyp kovariant ist.
// Covariant return type.
Func<Base, Base> f2 = f1;
Base b2 = f2(new Base());
' Covariant return type.
Dim f2 As Func(Of Base, Base) = f1
Dim b2 As Base = f2(New Base())
Der dritte Codeabschnitt zeigt, dass der Delegat einer Variablen vom Typ Func<Derived, Derived>
(Func(Of Derived, Derived)
in Visual Basic) zugewiesen werden kann, da der Parametertyp kontravariant ist.
// Contravariant parameter type.
Func<Derived, Derived> f3 = f1;
Derived d3 = f3(new Derived());
' Contravariant parameter type.
Dim f3 As Func(Of Derived, Derived) = f1
Dim d3 As Derived = f3(New Derived())
Der letzte Codeabschnitt zeigt, dass der Delegat einer Variablen vom Typ Func<Derived, Base>
(Func(Of Derived, Base)
in Visual Basic) zugewiesen werden kann, wobei die Effekte des kontravarianten Parametertyps und des kovarianten Rückgabetyps kombiniert werden.
// Covariant return type and contravariant parameter type.
Func<Derived, Base> f4 = f1;
Base b4 = f4(new Derived());
' Covariant return type and contravariant parameter type.
Dim f4 As Func(Of Derived, Base) = f1
Dim b4 As Base = f4(New Derived())
Abweichung bei nicht generischen Delegaten
Im vorherigen Code entspricht die Signatur von MyMethod
genau der Signatur des konstruierenden generischen Delegaten Func<Base, Derived>
(Func(Of Base, Derived)
in Visual Basic). Das Beispiel zeigt, dass dieser generische Delegat in Variablen oder Methodenparametern gespeichert werden kann, die über abgeleitete Parametertypen und weniger abgeleitete Rückgabetypen verfügen, solange alle Delegattypen aus dem generischen Delegattyp Func<T,TResult>erstellt werden.
Dies ist ein wichtiger Punkt. Kovarianz und Kontravarianz haben in den Typparametern generischer Delegaten ähnliche Auswirkungen wie bei der normalen Delegatbindung (siehe Varianz in Delegaten (C#) und Varianz in Delegaten (Visual Basic)). Die Varianz bei der Delegatbindung funktioniert jedoch bei allen Delegattypen und nicht nur bei generischen Delegattypen, die über variante Typparameter verfügen. Darüber hinaus ermöglicht die Abweichung in der Stellvertretungsbindung, dass eine Methode an jeden Delegaten gebunden werden kann, der restriktivere Parametertypen und einen weniger restriktiven Rückgabetyp aufweist, während die Zuweisung generischer Stellvertretungen nur funktioniert, wenn beide Delegattypen aus derselben generischen Typdefinition erstellt werden.
Das folgende Beispiel zeigt die kombinierten Auswirkungen der Varianz in der Stellvertretungsbindung und Varianz in generischen Typparametern. Im Beispiel wird eine Typhierarchie definiert, die drei Typen enthält, von den am wenigsten abgeleiteten (Type1
) bis zum meisten abgeleiteten (Type3
). Die Varianz bei der Bindung eines normalen Delegaten wird verwendet, um eine Methode mit einem Parametertyp Type1
und einem Rückgabewert vom Typ Type3
an einen generischen Delegaten mit einem Parametertyp Type2
und einem Rückgabewert vom Typ Type2
zu binden. Der resultierende generische Delegat wird dann einer anderen Variablen zugewiesen, deren generischer Delegattyp einen Parameter vom Typ Type3
und einen Rückgabetyp Type1
hat, unter Verwendung der Kovarianz und Kontravarianz von generischen Typparametern. Für die zweite Zuordnung muss sowohl der Variabletyp als auch der Delegattyp aus derselben generischen Typdefinition erstellt werden, in diesem Fall Func<T,TResult>.
using System;
public class Type1 {}
public class Type2 : Type1 {}
public class Type3 : Type2 {}
public class Program
{
public static Type3 MyMethod(Type1 t)
{
return t as Type3 ?? new Type3();
}
static void Main()
{
Func<Type2, Type2> f1 = MyMethod;
// Covariant return type and contravariant parameter type.
Func<Type3, Type1> f2 = f1;
Type1 t1 = f2(new Type3());
}
}
Public Class Type1
End Class
Public Class Type2
Inherits Type1
End Class
Public Class Type3
Inherits Type2
End Class
Public Class Program
Public Shared Function MyMethod(ByVal t As Type1) As Type3
Return If(TypeOf t Is Type3, t, New Type3())
End Function
Shared Sub Main()
Dim f1 As Func(Of Type2, Type2) = AddressOf MyMethod
' Covariant return type and contravariant parameter type.
Dim f2 As Func(Of Type3, Type1) = f1
Dim t1 As Type1 = f2(New Type3())
End Sub
End Class
Definieren von generischen Variantenschnittstellen und Delegaten
Visual Basic und C# verfügen über Schlüsselwörter, mit denen Sie die generischen Typparameter von Schnittstellen und Delegaten als kovariant oder kontravariant kennzeichnen können.
Ein kovarianter Typparameter wird mit dem out
Schlüsselwort (Out
Schlüsselwort in Visual Basic) markiert. Sie können einen kovarianten Typparameter als Rückgabewert einer Methode verwenden, die zu einer Schnittstelle gehört, oder als Rückgabetyp eines Delegaten. Sie können keinen kovarianten Typparameter als generische Typeinschränkung für Schnittstellenmethoden verwenden.
Hinweis
Wenn eine Methode einer Schnittstelle über einen Parameter verfügt, der ein generischer Delegattyp ist, kann ein kovarianter Typparameter des Schnittstellentyps verwendet werden, um einen kontravarianten Typparameter des Delegatentyps anzugeben.
Ein kontravarianter Typparameter wird mit dem in
Schlüsselwort (In
Schlüsselwort in Visual Basic) gekennzeichnet. Sie können einen kontravarianten Typparameter als Typ eines Parameters einer Methode verwenden, die zu einer Schnittstelle gehört, oder als Typ eines Parameters eines Delegaten. Sie können einen Kontravariantentypparameter als generische Typeinschränkung für eine Schnittstellenmethode verwenden.
Nur Schnittstellentypen und Delegattypen können Variantentypparameter aufweisen. Eine Schnittstelle oder ein Delegattyp kann sowohl kovariant als auch kontravariante Typparameter aufweisen.
Visual Basic und C# erlauben es Ihnen nicht, die Regeln für die Verwendung von Kovarianten- und Kontravariantentypparametern zu verletzen oder den Typparametern anderer Typen als Schnittstellen und Delegaten Kovarianz- und Kontravarianzanmerkungen hinzuzufügen.
Informationen und Beispielcode finden Sie unter Varianz in generischen Schnittstellen (C#) und Varianz in Generic Interfaces (Visual Basic).For information and example code, see Variance in Generic Interfaces (Visual Basic)
Liste der Typen
Die folgenden Schnittstellen- und Delegattypen weisen kovariante und/oder kontravariante Typparameter auf.
Typ | Kovariante Typparameter | Kontravariante Typparameter |
---|---|---|
Action<T> bis Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16> | Ja | |
Comparison<T> | Ja | |
Converter<TInput,TOutput> | Ja | Ja |
Func<TResult> | Ja | |
Func<T,TResult> bis Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult> | Ja | Ja |
IComparable<T> | Ja | |
Predicate<T> | Ja | |
IComparer<T> | Ja | |
IEnumerable<T> | Ja | |
IEnumerator<T> | Ja | |
IEqualityComparer<T> | Ja | |
IGrouping<TKey,TElement> | Ja | |
IOrderedEnumerable<TElement> | Ja | |
IOrderedQueryable<T> | Ja | |
IQueryable<T> | Ja |