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şkenineIEnumerable<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şkenineAction<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ündeList<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 Derived
olmalıdır. Yöntemin parametresi türünde Base
olduğ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)
veAction(Of Base)
Visual Basic'te) iki temsilciAction<Derived>
Action<Base>
verildiğinde, ikinci temsilciyi ilk temsilciyle birleştiremezsiniz, ancak sonuç tür güvenli olur. Varyans, ikinci temsilcinin türündeAction<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 PrintBases
IEnumerable<Base>
bir alır (IEnumerable(Of Base)
Visual Basic'te) ve öğeleri yazdırır. Derived
öğesinden Base
devralı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 (MustInherit
Visual 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).Compare
Visual 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 Shape
tü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ı Base
sınıfı, öğesini devralan Base
adlı Derived
bir sınıfı ve adlı bir yöntemiShared
(Visual Basic'te) MyMethod
olan başka bir static
sınıfı tanımlar. yöntemi bir örneğini Base
alır ve öğesinin bir örneğini Derived
döndürür. (Bağımsız değişken öğesinin Derived
MyMethod
bir örneğiyse, bunu döndürür; bağımsız değişken bir örneğiyse Base
MyMethod
yeni Derived
bir .) içindeMain()
, örnek öğesini temsil MyMethod
eden bir (Func(Of Base, Derived)
Visual Basic'te) örneği Func<Base, Derived>
oluşturur ve bunu değişkeninde f1
depolar.
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ü Type1
Type2
ve Type2
dö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 Type1
sahip 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 |