Genel değerlerde kovaryans ve kontravaryans

Kovaryans ve kontravaryans , başlangıçta belirtilenden daha fazla türetilmiş tür (daha belirgin) veya daha az türetilmiş bir tür (daha az spesifik) kullanma becerisine başvuran terimlerdir. Genel tür parametreleri, genel türleri atamakta ve kullanmakta daha fazla esneklik sağlamak için birlikte değişme ve değişken karşıtlığını destekler.

Bir tür sistemine başvururken kovaryans, kontravaryans ve sabitlik aşağıdaki tanımlara sahiptir. Örneklerde adlı bir temel sınıf ve adlı Base türetilmiş bir sınıf varsayılır Derived.

  • Covariance

    Başlangıçta belirtilenden daha türetilmiş bir tür kullanmanıza olanak tanır.

    türünde IEnumerable<Base>bir değişkenine IEnumerable<Derived> örneği atayabilirsiniz.

  • Contravariance

    Orijinal olarak belirtilenden daha genel (daha az türetilmiş) bir tür belirtmenize olanak tanır.

    türünde Action<Derived>bir değişkenine Action<Base> örneği atayabilirsiniz.

  • Invariance

    Yalnızca başlangıçta belirtilen türü kullanabileceğiniz anlamına gelir. Sabit genel tür parametresi ne kovaryan ne de değişkendeğişkendir.

    bir örneğini List<Base> türünde List<Derived> veya tam tersi bir değişkene atayamazsınız.

Kovaryant tür parametreleri, aşağıdaki kodda gösterildiği gibi sıradan Polimorfizme çok benzeyen atamalar yapmanıza olanak tanır.

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

List<T> sınıfı arabirimini IEnumerable<T> uygular, bu nedenle List<Derived> (List(Of Derived)Visual Basic'te) uygularIEnumerable<Derived>. Birlikte değişken tür parametresi geri kalanını yapar.

Diğer taraftan, birlikte değişkenlik mantığa aykırı görünüyor. Aşağıdaki örnek, türünde Action<Base> bir temsilci oluşturur (Action(Of Base) Visual Basic'te) ve ardından bu temsilciyi türünde Action<Derived>bir değişkene atar.

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())

Geri gibi görünüyor, ancak derleyen ve çalışan, tür açısından güvenli koddur. Lambda ifadesi atandığı temsilciyle eşleşir, bu nedenle türünde Base bir parametre alan ve dönüş değeri olmayan bir yöntem tanımlar. Temsilcinin tür Action<Derived> parametresi T değişkenden bağımsız olduğundan, sonuçta elde edilen temsilci türündeki bir değişkene Action<T> atanabilir. Bir parametre türü belirttiğinden T kod tür açısından güvenlidir. Türün Action<Base> temsilcisi, türünde bir temsilciymiş Action<Derived>gibi çağrıldığında, bağımsız değişkeni türünde Derivedolmalıdır. Yöntemin parametresi türünde Baseolduğundan, bu bağımsız değişken her zaman temel alınan yönteme güvenli bir şekilde geçirilebilir.

Genel olarak, birlikte değişken türünde bir parametre bir temsilcinin dönüş türü olarak kullanılabilir ve değişken karşıtı türü parametreler parametre türleri olarak kullanılabilir. Bir arabirim için, birlikte değişken türü parametreler arabirimin yöntemlerinin dönüş türleri olarak kullanılabilir ve değişken karşıtı türü parametreler arabirimin yöntemlerinin parametre türleri olarak kullanılabilir.

Kovaryans ve kontravaryans topluca varyans olarak adlandırılır. Kovaryant veya değişken karşıtı olarak işaretlenmemiş genel bir tür parametresi sabit olarak adlandırılır. Genel dil çalışma zamanında değişken ile ilgili gerçeklerin kısa bir özeti:

  • Değişken türü parametreleri genel arabirim ve genel temsilci türleriyle sınırlıdır.

  • Bir genel arabirim veya genel temsilci türünün hem birlikte değişen hem de değişken karşıtı parametreleri olabilir.

  • Değişken yalnızca başvuru türleri için geçerlidir; değişken türünde bir parametre için bir değer türü belirtirseniz, bu tür parametresi, sonuç olarak oluşturulan tür için değişmezdir.

  • Değişken, temsilci birleşimi için geçerli değildir. Diğer bir ifadeyle, ve (Action(Of Derived) ve Action(Of Base) Visual Basic'te) iki temsilci Action<Derived>Action<Base> verildiğinde, ikinci temsilciyi ilk temsilciyle birleştiremezsiniz, ancak sonuç tür güvenli olur. Varyans, ikinci temsilcinin türünde Action<Derived>bir değişkene atanmasını sağlar, ancak temsilciler yalnızca türleri tam olarak eşleşiyorsa birleştirilebilir.

  • C# 9'dan başlayarak, birlikte değişken dönüş türleri desteklenir. Geçersiz kılma yöntemi, geçersiz kılması yöntemi daha türetilmiş bir dönüş türü bildirebilir ve geçersiz kılma, salt okunur özelliği daha türetilmiş bir tür bildirebilir.

Ortak değişken türü parametrelerine sahip genel arabirimler

Çeşitli genel arabirimler, , IEnumerator<T>, IQueryable<T>ve IGrouping<TKey,TElement>gibi IEnumerable<T>ortak tür parametrelerine sahiptir. Bu arabirimlerin tüm tür parametreleri birlikte değişen olduğundan, tür parametreleri yalnızca üyelerin dönüş türleri için kullanılır.

Aşağıdaki örnekte, birlikte değişen tür parametreleri gösterilmektedir. Örnek iki türü tanımlar: Base adlı bir statik yöntemi vardır ve bu yöntem PrintBasesIEnumerable<Base> bir alır (IEnumerable(Of Base) Visual Basic'te) ve öğeleri yazdırır. Derived öğesinden Basedevralır. Örnek boş List<Derived> bir (List(Of Derived) Visual Basic'te) oluşturur ve bu türün tür değişkenine PrintBases geçirilebileceğini ve tür değişkenine IEnumerable<Base> atama olmadan atanabileceğini gösterir. List<T>IEnumerable<T>tek bir ortak değişken türü parametresine sahip olan öğesini uygular. birlikte değişken türü parametresi, yerine bir örneğinin IEnumerable<Derived> kullanılmasının IEnumerable<Base>nedenidir.

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

Değişken karşıtı tür parametrelerine sahip genel arabirimler

Çeşitli genel arabirimlerde değişken olmayan tür parametreleri vardır; örneğin: IComparer<T>, IComparable<T>ve IEqualityComparer<T>. Bu arabirimlerin yalnızca değişken karşıtı türde parametreleri olduğundan, tür parametreleri yalnızca arabirimlerin üyelerinde parametre türleri olarak kullanılır.

Aşağıdaki örnekte, değişken karşıtı tür parametreleri gösterilmektedir. Örnek, bir özelliği olan Area soyut (MustInheritVisual Basic'te) Shape sınıfını tanımlar. Örnek ayrıca (IComparer(Of Shape) Visual Basic'te) uygulayan IComparer<Shape> bir ShapeAreaComparer sınıf tanımlar. yönteminin IComparer<T>.Compare uygulanması özelliğin Area değerini temel alır, bu nedenle ShapeAreaComparer nesneleri alana göre sıralamak Shape için kullanılabilir.

Circle sınıfı devralır Shape ve geçersiz kılarArea. Örnek, (IComparer(Of Circle) Visual Basic'te) alan bir oluşturucu kullanarak nesnelerden oluşan Circle bir IComparer<Circle> oluştururSortedSet<T>. Ancak, örnek yerine bir IComparer<Circle>nesnesi geçirir ShapeAreaComparer ve bunu uygular IComparer<Shape>. Genel arabirimin tür parametresi değişken olmadığı için kod daha türetilmiş bir türünShape ( ) karşılaştırıcısını çağırdığında, örnek daha az türetilmiş bir türünCircle () karşılaştırıcısını IComparer<T> geçirebilir.

öğesine yeni Circle bir nesne eklendiğindeSortedSet<Circle>, IComparer<Shape>.Compare yeni öğe var olan bir öğeyle karşılaştırıldığında nesnenin ShapeAreaComparer yöntemi (IComparer(Of Shape).CompareVisual Basic'teki yöntem) çağrılır. Yöntemin parametre türü (Shape ) geçirilenCircle türden daha az türetilir, bu nedenle çağrı türü güvenlidir. Contravariance, herhangi bir tek türden bir koleksiyonun yanı sıra türünden Shapetüretilen karışık bir koleksiyon sıralamaya olanak tanırShapeAreaComparer.

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

Değişken türü parametreleri olan genel temsilciler

Func gibi Func<T,TResult>genel temsilciler, birlikte değişken dönüş türlerine ve değişken karşıtı parametre türlerine sahiptir. Action gibi Action<T1,T2>genel temsilcilerin değişken olmayan parametre türleri vardır. Bu, temsilcilerin daha fazla türetilmiş parametre türüne ve (genel temsilciler söz konusu olduğunda) daha az türetilmiş dönüş türüne Func sahip değişkenlere atanabileceği anlamına gelir.

Not

Genel temsilcilerin Func son genel tür parametresi, temsilci imzasında dönüş değerinin türünü belirtir. Ortak değişkendir (out anahtar sözcük), diğer genel tür parametreleri ise değişkensizdir (in anahtar sözcük).

Aşağıdaki kodda bu gösterilir. İlk kod parçası, adlı Basesınıfı, öğesini devralan Baseadlı Derived bir sınıfı ve adlı bir yöntemiShared (Visual Basic'te) MyMethodolan başka bir static sınıfı tanımlar. yöntemi bir örneğini Base alır ve öğesinin bir örneğini Deriveddöndürür. (Bağımsız değişken öğesinin DerivedMyMethod bir örneğiyse, bunu döndürür; bağımsız değişken bir örneğiyse BaseMyMethod yeni Derivedbir .) içindeMain(), örnek öğesini temsil MyMethodeden bir (Func(Of Base, Derived) Visual Basic'te) örneği Func<Base, Derived> oluşturur ve bunu değişkeninde f1depolar.

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

İkinci kod parçası, dönüş türü birlikte değişken olduğundan temsilcinin türünde Func<Base, Base>Func(Of Base, Base) (Visual Basic'te) bir değişkene atanabileceğini gösterir.

// 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())

Üçüncü kod parçası, parametre türü değişkenden bağımsız olduğundan temsilcinin türünde Func<Derived, Derived>Func(Of Derived, Derived) (Visual Basic'te) bir değişkene atanabileceğini gösterir.

// 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())

Kodun son parçası, temsilcinin değişken karşıtı parametre türünün Func<Derived, Base>Func(Of Derived, Base) ve kovaryan dönüş türünün etkilerini birleştirerek türünde (Visual Basic'te) bir değişkene atanabileceğini gösterir.

// 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())

Genel olmayan temsilcilerde varyans

Yukarıdaki kodda imzası, MyMethod oluşturulan genel temsilcinin imzası ile tam olarak eşleşir: Func<Base, Derived> (Func(Of Base, Derived) Visual Basic'te). Örnekte, tüm temsilci türleri genel temsilci türünden oluşturulacağı sürece, bu genel temsilcinin daha fazla türetilmiş parametre türüne ve daha az türetilmiş dönüş türüne sahip değişkenlerde veya yöntem parametrelerinde Func<T,TResult>depolanabileceği gösterilir.

Bu önemli bir noktadır. Genel temsilcilerin tür parametrelerinde kovaryans ve kontravaryans etkileri, normal temsilci bağlamasında kovaryans ve değişken karşıtının etkilerine benzer (bkz . Temsilcilerde Varyans (C#) ve Temsilcilerde Varyans (Visual Basic)). Ancak, temsilci bağlamadaki değişken, yalnızca değişken türde parametreleri olan genel temsilci türleriyle değil, tüm temsilci türleriyle birlikte çalışır. Ayrıca, temsilci bağlamada değişken, bir yöntemin daha kısıtlayıcı parametre türleri ve daha az kısıtlayıcı dönüş türü olan herhangi bir temsilciye bağlanmasına olanak tanırken, genel temsilcilerin atanması yalnızca her iki temsilci türünün de aynı genel tür tanımından oluşturulması durumunda çalışır.

Aşağıdaki örnekte, temsilci bağlamadaki değişkenin ve genel tür parametrelerindeki değişkenin birleşik etkisi gösterilmektedir. Örnek, en az türetilenden () en çok türetilene (Type1) kadarType3 üç tür içeren bir tür hiyerarşisi tanımlar. Normal temsilci bağlamasında varyans, parametre türü ve dönüş türü olan bir yöntemi, parametre türü Type1Type2 ve Type2dönüş türü Type3 olan genel bir temsilciye bağlamak için kullanılır. Sonuçta elde edilen genel temsilci, genel temsilci türü parametresine Type3 ve dönüş türüne Type1sahip olan başka bir değişkene, genel tür parametrelerinin kovaryans ve değişken karşıtı kullanılarak atanır. İkinci atama, hem değişken türünün hem de temsilci türünün aynı genel tür tanımından (bu örnekte Func<T,TResult>) oluşturulup oluşturulamaya gerek duymasını gerektirir.

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

Değişken genel arabirimleri ve temsilcileri tanımlama

Visual Basic ve C# arabirimlerin ve temsilcilerin genel tür parametrelerini birlikte değişken veya değişkensiz olarak işaretlemenizi sağlayan anahtar sözcüklere sahiptir.

Birlikte değişken türü parametresi anahtar sözcüğüyle out (Out Visual Basic'te anahtar sözcük) işaretlenir. Birlikte değişken türünde bir parametreyi, bir arabirime ait olan bir yöntemin dönüş değeri olarak veya bir temsilcinin dönüş türü olarak kullanabilirsiniz. Birlikte değişken türünde bir parametreyi, arabirim yöntemleri için genel türde bir kısıtlayıcı olarak kullanamazsınız.

Not

Bir arabirimin bir yöntemi genel temsilci türünde bir parametreye sahipse, arabirim türünün birlikte değişken türünde bir parametresi, temsilci türünün değişken karşıtı türünde bir parametresini belirtmek için kullanılabilir.

Değişken karşıtı tür parametresi anahtar sözcüğüyle in (In Visual Basic'te anahtar sözcük) işaretlenir. Değişken karşıtı türde bir parametreyi, bir arabirime ait olan bir yöntemin bir parametresinin türü olarak veya bir temsilcinin bir parametresinin türü olarak kullanabilirsiniz. Değişken karşıtı türde bir parametreyi, bir arabirim yöntemi için genel türde bir kısıtlama olarak kullanabilirsiniz.

Yalnızca arabirim türlerinin ve temsilci türlerinin değişken türünde parametreleri olabilir. Bir arabirim veya temsilci türünün hem birlikte değişen hem de değişken karşıtı parametreleri olabilir.

Visual Basic ve C#, birlikte değişken veya değişken karşıtı türde parametrelerin kullanılmasına ilişkin kuralların ihlal edilmesine veya arabirimlerden ve temsilcilerden başka türde tür parametrelerine birlikte değişken veya değişken karşıtı ek açıklamalar eklenmesine izin vermez.

Bilgi ve örnek kod için bkz . Genel Arabirimlerde Varyans (C#) ve Genel Arabirimlerde Varyans (Visual Basic).

Türlerin listesi

Aşağıdaki arabirim ve temsilci türleri ortak değişken ve/veya değişken karşıtı tür parametrelerine sahiptir.

Tür Birlikte değişken türde parametreler Değişken karşıtı türde parametreler
Action<T> - Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16> Yes
Comparison<T> Evet
Converter<TInput,TOutput> Evet Evet
Func<TResult> Yes
Func<T,TResult> - Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult> Yes Evet
IComparable<T> Evet
Predicate<T> Evet
IComparer<T> Evet
IEnumerable<T> Evet
IEnumerator<T> Evet
IEqualityComparer<T> Evet
IGrouping<TKey,TElement> Evet
IOrderedEnumerable<TElement> Evet
IOrderedQueryable<T> Evet
IQueryable<T> Yes

Ayrıca bkz.