Megosztás a következőn keresztül:


Kovariancia és contravariance a genericsban

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.

    Egy példányt IEnumerable<Derived> hozzárendelhet egy típusú IEnumerable<Base>változóhoz.

  • Contravariance

    Lehetővé teszi az eredetileg megadottnál általánosabb (kevésbé származtatott) típus használatát.

    Egy példányt Action<Base> hozzárendelhet egy típusú Action<Derived>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 példányt List<Base> egy típusú List<Derived> változóhoz, vagy fordítva.

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ál.IEnumerable<Derived> A kovariant típusú paraméter elvégzi a többit.

A contravariance viszont ellenintuitívnak 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ítja és futtatja. 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 Action<T> delegált típusparamétere 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 Derivedkell 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 kovarianciát és az ellenvarianciát együttesen varianciának nevezzük. A kovariant vagy a contravariant jelöléssel nem rendelkező általános típusparamétert invariantnak nevezzük. A gyakori 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 két típusú Action<Derived>Action<Base> delegáltat és (Action(Of Derived) és Action(Of Base) a Visual Basicben) nem kombinálhatja a második delegáltat az elsővel, bár az eredmény biztonságos típus 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ódusok a felülbírált metódussal származtatottabb visszatérési típust deklarálhatnak, a felülbíráló, írásvédett tulajdonság pedig származtatottabb típust deklarálhat.

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 van egy statikus metódus nevePrintBases, amely egy (IEnumerable(Of Base)a Visual Basicben) részt vesz IEnumerable<Base> , és kinyomtatja az elemeket. Derived örökli a .-tól Base. A példa létrehoz egy üreset List<Derived> (List(Of Derived) a Visual Basicben), és bemutatja, hogy ez a típus öntés nélkül adható át PrintBases és rendelhető hozzá egy változóhoz IEnumerable<Base> . 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 (Visual Basicben) implementálhatóIComparer(Of Shape)IComparer<Shape> osztályt ShapeAreaComparer is definiál. A metódus megvalósítása a IComparer<T>.Compare tulajdonság értékén Area alapul, így ShapeAreaComparer az objektumok terület szerinti rendezésére Shape használható.

Az Circle osztály örökli Shape és felülbírálja a parancsot Area. A példa objektumokat hoz létre SortedSet<T>Circle egy olyan konstruktor használatával, amely (IComparer(Of Circle)a Visual Basicben) egy objektumot IComparer<Circle> vesz igénybe. A példa ShapeAreaComparer azonban ahelyett, hogy átad egy IComparer<Circle>objektumot, amely megvalósítja IComparer<Shape>a műveletet. 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>.CompareShapeAreaComparer objektum metódusát (IComparer(Of Shape).Comparemetó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 contravariance lehetővé tesziShapeAreaComparer, hogy bármilyen típusú gyűjteményt, valamint vegyes típusok gyűjteményét rendezze, amelyek származnak.Shape

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 meghatalmazottak, például Func<T,TResult>a kovariant visszatérési típusok és a contravariant paramétertípusok. Az Action általános meghatalmazottak, például Action<T1,T2>a contravariant paramétertípusokkal rendelkeznek. Ez azt jelenti, hogy a delegáltak olyan változókhoz rendelhetők, amelyek több származtatott paramétertípussal rendelkeznek, és (általános Func meghatalmazottak esetén) kevésbé származtatott visszatérési típusok.

Feljegyzé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ó).

Az alábbi kód ezt mutatja be. Az első kódrészlet definiál egy osztályt Base, egy öröklő DerivedBaseosztályt, egy másik osztályt pedig egy static metódussal (Shared a Visual Basicben) elnevezve MyMethod. A metódus egy példányt Base vesz fel, és egy példányt Derivedad vissza. (Ha az argumentum a példányaDerived, MyMethod azt adja vissza; ha az argumentum a példányaBase, MyMethod akkor a függvény egy új példányt Derivedad vissza.) Ebben Main()a példában a (Visual Basicben) egy példányt Func<Base, Derived>Func(Of Base, Derived) hoz létre, amely a változót f1jelöli MyMethodés 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 meghatalmazottakban

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 contravariance hatása az általános meghatalmazottak típusparamétereiben hasonló a kovariancia és a contravariance normál delegálási kötésben (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 egy metódus paramétertípussal Type1 és egy általános delegálthoz való visszatérési Type3 típussal történő kötésére szolgál, amelynek paramétertípusa Type2 és visszatérési Type2típusa. 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 Type1rendelkezik, 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 covariant típusú paraméterek a kulcsszóval (Outa Visual Basic kulcsszójávalout) vannak megjelölve. 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.

Feljegyzé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 contravariant típusú paraméter a kulcsszóval (Ina in Visual Basic kulcsszójával) van megjelölve. 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> / 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> / 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

Lásd még