Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A kovariancia és az ellenvariancia olyan kifejezések, amelyek az eredetileg megadottnál származtatottabb (pontosabb) vagy kevésbé származtatott (kevésbé specifikus) típus használatára utalnak. Az általános típusparaméterek támogatják a kovarianciát és a contravariance-t, hogy nagyobb rugalmasságot biztosítsanak az általános típusok hozzárendelésében és használatában.
Amikor típusrendszerre hivatkozik, a kovariancia, a contravariance és az invariance a következő definíciókkal rendelkezik. A példák egy elnevezett Base
alaposztályt és egy származtatott osztályt feltételeznek Derived
.
Covariance
Lehetővé teszi, hogy az eredetileg megadottnál származtatottabb típust használjon.
Hozzárendelhet egy
IEnumerable<Derived>
példányt egyIEnumerable<Base>
típusú változóhoz.Contravariance
Lehetővé teszi az eredetileg megadottnál általánosabb (kevésbé származtatott) típus használatát.
Hozzárendelhet egy
Action<Base>
példányt egyAction<Derived>
típusú változóhoz.Invariance
Azt jelenti, hogy csak az eredetileg megadott típust használhatja. Az invariáns általános típusparaméter nem covariant vagy contravariant.
Nem rendelhet egy
List<Base>
példányt egyList<Derived>
típusú változóhoz, vagy fordítva sem.
A covariant típusú paraméterek lehetővé teszik olyan hozzárendelések elvégzését, amelyek a szokásos polimorfizmushoz hasonlóan néznek ki, ahogyan az az alábbi kódban is látható.
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
Az List<T> osztály implementálja az IEnumerable<T> interfészt, így List<Derived>
(List(Of Derived)
a Visual Basicben) implementálja IEnumerable<Derived>
. A kovariant típusú paraméter elvégzi a többit.
A kontravariancia viszont ellentmondásosnak tűnik. Az alábbi példa létrehoz egy típusú Action<Base>
delegáltat (Action(Of Base)
a Visual Basicben), majd hozzárendeli a delegáltat egy típusváltozóhoz Action<Derived>
.
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())
Ez visszafelé néz ki, de ez egy olyan típusbiztos kód, amely lefordítódik és fut. A lambda kifejezés megegyezik a hozzárendelt delegálttal, ezért egy olyan metódust határoz meg, amely egy típusú Base
paramétert használ, és amelynek nincs visszatérési értéke. Az eredményül kapott delegált hozzárendelhető egy típusváltozóhozAction<Derived>
, mert a T
delegált típusparamétere Action<T> contravariant. A kód típusbiztos, mert T
paramétertípust ad meg. Ha a típusmegbízott Action<Base>
úgy van meghívva, mintha típusmegbízott Action<Derived>
lenne, az argumentumának típusnak Derived
kell lennie . Ez az argumentum mindig biztonságosan továbbítható a mögöttes metódusnak, mert a metódus paramétere típus.Base
Általában egy kovariant típusú paraméter használható delegált visszatérési típusaként, a contravariant típusú paraméterek pedig paramétertípusokként. Egy interfész esetében a kovariant típusú paraméterek használhatók az interfész metódusainak visszatérési típusaiként, a contravariant típusú paraméterek pedig az interfész metódusainak paramétertípusaiként.
A kovariancia és az ellenvariancia együttes elnevezése variancia. A kovariant vagy a contravariant jelöléssel nem rendelkező általános típusparamétert invariantnak nevezzük. A közös nyelvi futtatókörnyezet varianciájával kapcsolatos tények rövid összefoglalása:
A variánstípus-paraméterek az általános felületre és az általános delegálási típusra korlátozódnak.
Az általános illesztő- vagy általános delegálási típus kovarianit és contravariant típusú paraméterekkel is rendelkezhet.
A variancia csak referenciatípusokra vonatkozik; ha értéktípust ad meg egy variánstípus-paraméterhez, az adott típusparaméter invariáns az eredményként létrehozott típushoz.
A variancia nem vonatkozik a delegált kombinációra. Ez azt jelenti, hogy ha adott két delegátum,
Action<Derived>
ésAction<Base>
típusú (Action(Of Derived)
ésAction(Of Base)
a Visual Basicben), nem kombinálhatja a második delegátumot az elsővel, bár az eredmény típusbiztos lenne. A variancia lehetővé teszi, hogy a második delegált egy típusúAction<Derived>
változóhoz legyen rendelve, de a meghatalmazottak csak akkor egyesíthetők, ha a típusok pontosan egyeznek.A C# 9-től kezdődően a kovátriai visszatérési típusok támogatottak. A felülbíráló metódus deklarálhat a felülbírált metódusnál származtatottabb visszatérési típust, és a felülbíráló, írásvédett tulajdonság is deklarálhat származtatottabb típust.
Kovariant típusú paraméterekkel rendelkező általános interfészek
Számos általános felület kovariant típusú paraméterekkel rendelkezik, IEnumerable<T>például , IEnumerator<T>, IQueryable<T>és IGrouping<TKey,TElement>. Ezeknek az interfészeknek az összes típusparamétere kovariant, így a típusparaméterek csak a tagok visszatérési típusaihoz használhatók.
Az alábbi példa a kovariant típusú paramétereket mutatja be. A példa két típust határoz meg: Base
egy statikus metódus neve PrintBases
, amely egy IEnumerable<Base>
(IEnumerable(Of Base)
a Visual Basicben) paramétert vesz át, és kinyomtatja az elemeket.
Derived
örököl Base
-től. A példa létrehoz egy üres List<Derived>
objektumot (List(Of Derived)
a Visual Basicben), és bemutatja, hogy ez a típus átalakítás nélkül átadható a PrintBases
-nek, és hozzárendelhető egy IEnumerable<Base>
típusú változóhoz.
List<T> implementálja IEnumerable<T>, amely egyetlen kovariant típusú paraméterrel rendelkezik. A kovariant típusú paraméter az oka annak, hogy egy példány IEnumerable<Derived>
használható helyett IEnumerable<Base>
.
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
Általános felületek contravariant típusú paraméterekkel
Számos általános adapter rendelkezik contravariant típusú paraméterekkel; például: IComparer<T>, IComparable<T>és IEqualityComparer<T>. Ezek az interfészek csak contravariant típusú paraméterekkel rendelkeznek, így a típusparaméterek csak paramétertípusként használhatók az interfészek tagjaiban.
Az alábbi példa a contravariant típusú paramétereket mutatja be. A példa egy absztrakt (MustInherit
Visual Basic) Shape
osztályt határoz meg egy Area
tulajdonsággal. A példa egy ShapeAreaComparer
osztályt is definiál, amely megvalósítja a IComparer<Shape>
-t (IComparer(Of Shape)
a Visual Basicben). A IComparer<T>.Compare metódus megvalósítása a Area
tulajdonság értékén alapul, így ShapeAreaComparer
a terület szerinti rendezésére használható Shape
objektumok.
Az Circle
osztály örökli a Shape
és felülbírálja a Area
metódust. A példa egy SortedSet<T> hoz létre Circle
objektumokból, egy olyan konstruktor használatával, amely IComparer<Circle>
-t vesz igénybe (IComparer(Of Circle)
a Visual Basicben). A példa azonban ahelyett, hogy egy IComparer<Circle>
-t adna át, egy ShapeAreaComparer
objektumot ad át, amely megvalósítja IComparer<Shape>
-t. A példa átadhat egy kevésbé származtatott (Shape
) típusú összehasonlítót, amikor a kód egy származtatottabb (Circle
) típusú összehasonlítót hív meg, mivel az IComparer<T> általános felület típusparamétere contravariant.
Amikor új Circle
objektumot ad hozzá az SortedSet<Circle>
objektumhoz, a rendszer minden alkalommal meghívja az IComparer<Shape>.Compare
IComparer(Of Shape).Compare
objektum metódusát (ShapeAreaComparer
metódusát a Visual Basicben), amikor az új elemet egy meglévő elemhez hasonlítják. A metódus (Shape
) paramétertípusa kevésbé származtatott, mint az átadott (Circle
) típus, így a hívás típusa biztonságos. A kontravariancia lehetővé teszi ShapeAreaComparer
számára, hogy bármilyen típusú gyűjteményt, valamint egy vegyes típusokból álló gyűjteményt is rendezzen, melyek a Shape
típusból származnak.
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
Általános meghatalmazottak variánstípus-paraméterekkel
Az Func
általános delegátumok, például Func<T,TResult>, a kovariáns visszatérési típusokkal és a kontravariáns paramétertípusokkal rendelkeznek. Az Action
általános delegátusok, például Action<T1,T2>, kontravariáns paramétertípusúak. Ez azt jelenti, hogy a delegáltak olyan változókhoz rendelhetők, amelyek több származtatott paramétertípussal rendelkeznek, és (az Func
általános delegáltak esetében) kevésbé származtatott visszatérési típusokkal.
Megjegyzés:
Az általános meghatalmazottak utolsó általános típusparamétere Func
határozza meg a delegált aláírás visszatérési értékének típusát. Ez covariant (out
kulcsszó), míg a többi általános típusparaméter a contravariant (in
kulcsszó).
Ezt az alábbi kód szemlélteti. Az első kódrészlet definiál egy Base
nevű osztályt, egy Derived
-t öröklő Base
nevű osztályt, és egy másik osztályt static
nevű Shared
metódussal (MyMethod
a Visual Basicben). A metódus egy Base
példányt fogad, és egy Derived
példányt ad vissza. (Ha az argumentum a példánya Derived
, MyMethod
azt adja vissza; ha az argumentum a példánya Base
, MyMethod
akkor a függvény egy új példányt Derived
ad vissza.) Ebben a Main()
példában a Visual Basicben Func<Base, Derived>
, egy példányt Func(Of Base, Derived)
hoz létre, amely MyMethod
jelöli, és a változóban f1
tárolja.
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
A második kódrészlet azt mutatja, hogy a delegált hozzárendelhető egy típusú Func<Base, Base>
változóhoz (Func(Of Base, Base)
a Visual Basicben), mert a visszatérési típus kovariant.
// 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())
A harmadik kódrészlet azt mutatja, hogy a delegált hozzárendelhető egy típusú Func<Derived, Derived>
változóhoz (Func(Of Derived, Derived)
a Visual Basicben), mert a paraméter típusa contravariant.
// 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())
Az utolsó kódrészlet azt mutatja, hogy a delegált hozzárendelhető egy típusváltozóhoz Func<Derived, Base>
(Func(Of Derived, Base)
a Visual Basicben), kombinálva a contravariant paramétertípus és a kovariant visszatérési típus hatásait.
// 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())
Variancia nem általános delegáltak esetében
Az előző kódban az aláírás MyMethod
pontosan megegyezik a létrehozott általános meghatalmazott aláírásával: Func<Base, Derived>
(Func(Of Base, Derived)
a Visual Basicben). A példa azt mutatja, hogy ez az általános delegált olyan változókban vagy metódusparaméterekben tárolható, amelyek több származtatott paramétertípussal és kevésbé származtatott visszatérési típussal rendelkeznek, feltéve, hogy az összes delegálttípus az általános delegálási típusból Func<T,TResult>lett létrehozva.
Ez egy fontos pont. A kovariancia és a kontravariancia hatása a generikus delegáltak típusparamétereiben hasonló a kovariancia és a kontravariancia szokványos delegált kötésnél (lásd Variance in Delegates (C#) és Variance in Delegates (Visual Basic)). A delegált kötés varianciája azonban minden delegálttípussal működik, nem csak az általános delegálási típusok esetében, amelyek variánstípus-paraméterekkel rendelkeznek. Emellett a delegáltkötések varianciája lehetővé teszi, hogy a metódusok minden olyan delegálthoz kötődjenek, amely szigorúbb paramétertípusokkal és kevésbé korlátozó visszatérési típussal rendelkezik, míg az általános meghatalmazottak hozzárendelése csak akkor működik, ha mindkét delegálttípus ugyanabból az általános típusdefinícióból lett létrehozva.
Az alábbi példa a variancia együttes hatását mutatja be a delegált kötésben és a varianciában az általános típusparaméterekben. A példa egy típushierarchiát határoz meg, amely három típust tartalmaz, a legkevésbé származtatott (Type1
) típustól a legtöbb származtatottig (Type3
). A szokásos delegált kötés varianciája azt a célt szolgálja, hogy egy Type1
paramétertípusú és Type3
visszatérési típusú metódust köt össze egy általános delegálttal, amelynek paramétertípusa Type2
és visszatérési típusa Type2
. Az eredményül kapott általános delegált ezután egy másik változóhoz lesz hozzárendelve, amelynek általános delegálási típusa típusparaméterrel Type3
és visszatérési típussal Type1
rendelkezik, az általános típusú paraméterek kovarianciájának és ellentravarianciájának használatával. A második hozzárendeléshez a változótípust és a delegálttípust is ugyanabból az általános típusdefinícióból kell létrehozni, Func<T,TResult>ebben az esetben.
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
Variáns általános interfészek és delegáltak definiálása
A Visual Basic és a C# kulcsszavak lehetővé teszik az interfészek és meghatalmazottak általános típusparamétereinek kovariantikus vagy contravariantként való megjelölését.
A kovariáns típusparaméter a out
kulcsszóval van megjelölve (Out
kulcsszóval Visual Basicben). A kovariáns típusú paramétert használhatja egy felülethez tartozó metódus visszatérési értékeként, vagy meghatalmazott visszatérési típusaként. A kovarians típusparaméter nem használható általános típuskényszerként az interfészmetelyekhez.
Megjegyzés:
Ha egy interfész valamely metódusa egy általános delegált típusú paraméterrel rendelkezik, a felülettípus kovariáns típusú paramétere használható a delegált típus contravariant típusú paraméterének megadására.
A kontravariáns típusparaméter a in
kulcsszóval van megjelölve (In
kulcsszó Visual Basic-ben). Használhatja a contravariant típusú paramétert egy felülethez tartozó metódus paraméterének típusaként, vagy egy meghatalmazott paraméterének típusaként. A contravariant típusú paramétert általános típuskényszerként használhatja egy interfészmetódushoz.
Csak a felülettípusok és a delegált típusok rendelkezhetnek változattípus-paraméterekkel. Az interfész- vagy delegálástípusnak lehetnek kovariant és contravariant típusú paraméterei is.
A Visual Basic és a C# nem teszi lehetővé, hogy megsértse a kovariáns és a contravariant típusú paraméterek használatára vonatkozó szabályokat, vagy hogy kovariancia- és ellenvariancia-megjegyzéseket adjon hozzá az interfészeken és delegáltakon kívüli típusok típusparamétereihez.
További információ és példakód: Variance in Generic Interfaces (C#) and Variance in Generic Interfaces (Visual Basic).
Típuslista
Az alábbi interfész- és delegálástípusok kovariant és/vagy contravariant típusú paraméterekkel rendelkeznek.
típus | Covariant-típusparaméterek | Contravariant típusú paraméterek |
---|---|---|
Action<T> és Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16> | Igen | |
Comparison<T> | Igen | |
Converter<TInput,TOutput> | Igen | Igen |
Func<TResult> | Igen | |
Func<T,TResult> és Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult> | Igen | Igen |
IComparable<T> | Igen | |
Predicate<T> | Igen | |
IComparer<T> | Igen | |
IEnumerable<T> | Igen | |
IEnumerator<T> | Igen | |
IEqualityComparer<T> | Igen | |
IGrouping<TKey,TElement> | Igen | |
IOrderedEnumerable<TElement> | Igen | |
IOrderedQueryable<T> | Igen | |
IQueryable<T> | Igen |