Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Kovariansi dan kontravariansi adalah istilah yang mengacu pada kemampuan untuk menggunakan jenis yang lebih turunan (lebih spesifik) atau jenis yang kurang diturunkan (kurang spesifik) daripada yang ditentukan awalnya. Parameter jenis generik mendukung kovariansi dan kontravariansi untuk memberikan fleksibilitas yang lebih besar dalam menetapkan dan menggunakan jenis generik.
Ketika Anda merujuk ke sistem tipe, kovarians, kontravariansi, dan invarians memiliki definisi sebagai berikut. Contoh mengasumsikan kelas dasar bernama Base
dan kelas turunan bernama Derived
.
Covariance
Memungkinkan Anda menggunakan jenis yang lebih turunan dari yang semula ditentukan.
Anda dapat menetapkan instans
IEnumerable<Derived>
ke variabel jenisIEnumerable<Base>
.Contravariance
Memungkinkan Anda menggunakan tipe yang lebih umum (kurang spesifik) dari yang semula ditentukan.
Anda dapat menetapkan instans
Action<Base>
ke variabel jenisAction<Derived>
.Invariance
Berarti Anda hanya dapat menggunakan jenis yang awalnya ditentukan. Parameter jenis generik invarian bukan kovarian atau kontravarian.
Anda tidak dapat menetapkan instans
List<Base>
ke variabel jenisList<Derived>
atau sebaliknya.
Parameter jenis kovarian memungkinkan Anda membuat tugas yang terlihat seperti Polimorfisme biasa, seperti yang ditunjukkan dalam kode berikut.
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
Kelas List<T> mengimplementasikan IEnumerable<T> antarmuka, jadi List<Derived>
(List(Of Derived)
dalam Visual Basic) mengimplementasikan IEnumerable<Derived>
. Parameter jenis kovarian menangani sisanya.
Kontravariansi, di sisi lain, tampaknya berlawanan. Contoh berikut membuat delegasi jenis Action<Base>
(Action(Of Base)
di Visual Basic), lalu menetapkan delegasi tersebut ke variabel jenis 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())
Ini tampaknya mundur, tetapi ini adalah kode jenis aman yang mengkompilasi dan menjalankan. Ekspresi lambda cocok dengan delegasi yang ditetapkan, sehingga mendefinisikan metode yang mengambil satu parameter jenis Base
dan yang tidak memiliki nilai pengembalian. Delegasi yang dihasilkan dapat ditetapkan ke variabel bertipe Action<Derived>
karena parameter tipe T
dari delegasi Action<T> bersifat kontravarian. Kode aman tipe karena T
menetapkan tipe parameter. Ketika delegasi jenis Action<Base>
dipanggil seolah-olah itu adalah delegasi jenis Action<Derived>
, argumennya harus berjenis Derived
. Argumen ini selalu dapat diteruskan dengan aman ke metode yang mendasar, karena parameter metode berjenis Base
.
Secara umum, parameter tipe kovarian dapat digunakan sebagai tipe pengembalian dari sebuah delegasi, dan parameter tipe kontravarian dapat digunakan sebagai tipe parameter. Untuk antarmuka, parameter jenis kovarian dapat digunakan sebagai jenis pengembalian metode antarmuka, dan parameter jenis kontravarian dapat digunakan sebagai jenis parameter metode antarmuka.
Kovariansi dan kontravariansi secara kolektif disebut sebagai varian. Parameter jenis generik yang tidak ditandai kovarian atau kontravarian disebut sebagai invarian. Ringkasan singkat fakta tentang variasi dalam runtime bahasa pemrograman umum.
Parameter jenis varian dibatasi untuk antarmuka generik dan jenis delegasi generik.
Antarmuka generik atau jenis delegasi generik dapat memiliki parameter jenis kovarian dan kontravarian.
Varians hanya berlaku untuk jenis referensi; jika Anda menentukan jenis nilai untuk parameter jenis varian, parameter jenis tersebut invariant untuk jenis konstruksi yang dihasilkan.
Varians tidak berlaku untuk kombinasi delegasi. Artinya, mengingat dua delegasi jenis
Action<Derived>
danAction<Base>
(Action(Of Derived)
danAction(Of Base)
di Visual Basic), Anda tidak dapat menggabungkan delegasi kedua dengan yang pertama meskipun hasilnya akan aman. Varians memungkinkan delegasi kedua ditetapkan ke variabel jenisAction<Derived>
, tetapi delegasi hanya dapat menggabungkan jika jenisnya cocok dengan persis.Mulai dari C# 9, jenis pengembalian kovarian didukung. Metode overriding dapat menyatakan tipe pengembalian yang lebih spesifik dari metode yang di-overriding, dan properti read-only yang di-overriding dapat mendeklarasikan tipe yang lebih spesifik.
Antarmuka generik dengan parameter jenis kovarian
Beberapa antarmuka generik memiliki parameter jenis kovarian, misalnya, , IEnumerable<T>, IEnumerator<T>IQueryable<T>, dan IGrouping<TKey,TElement>. Semua parameter jenis antarmuka ini adalah kovarian, oleh karena itu, parameter jenis hanya digunakan untuk jenis pengembalian dari anggota.
Contoh berikut mengilustrasikan parameter tipe kovarian. Contoh mendefinisikan dua jenis: Base
memiliki metode statis bernama PrintBases
yang mengambil IEnumerable<Base>
(IEnumerable(Of Base)
di Visual Basic) dan mencetak elemen.
Derived
mewarisi dari Base
. Contoh membuat objek kosong List<Derived>
(List(Of Derived)
di Visual Basic) dan menunjukkan bahwa jenis ini dapat diteruskan ke PrintBases
dan dialokasikan ke variabel jenis IEnumerable<Base>
tanpa perlu konversi.
List<T> mengimplementasikan IEnumerable<T>, yang memiliki parameter jenis kovarian tunggal. Parameter jenis kovarian adalah alasan mengapa instans IEnumerable<Derived>
dapat digunakan alih-alih 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
Antarmuka generik dengan parameter jenis kontravarian
Beberapa antarmuka generik memiliki parameter jenis kontravarian; misalnya: IComparer<T>, IComparable<T>, dan IEqualityComparer<T>. Antarmuka ini hanya memiliki parameter jenis kontravarian, sehingga parameter jenis hanya digunakan sebagai jenis parameter dalam anggota antarmuka.
Contoh berikut mengilustrasikan parameter jenis kontravarian. Contoh mendefinisikan kelas abstrak (MustInherit
di Visual Basic) Shape
dengan Area
properti . Contoh ini juga mendefinisikan ShapeAreaComparer
kelas yang mengimplementasikan IComparer<Shape>
(IComparer(Of Shape)
di Visual Basic). Implementasi IComparer<T>.Compare metode didasarkan pada nilai Area
properti, sehingga ShapeAreaComparer
dapat digunakan untuk mengurutkan Shape
objek berdasarkan area.
Kelas Circle
mewarisi Shape
dan mengambil alih Area
. Contoh ini membuat SortedSet<T> dari Circle
objek, menggunakan konstruktor yang menerima IComparer<Circle>
(IComparer(Of Circle)
dalam Visual Basic). Namun, alih-alih meneruskan IComparer<Circle>
, contoh melewati objek ShapeAreaComparer
, yang mengimplementasikan IComparer<Shape>
. Contoh dapat memanfaatkan pembanding dari jenis yang kurang lebih turunan (Shape
) ketika kode memanggil pembanding dari jenis yang lebih diturunkan (Circle
), karena parameter jenis antarmuka generik yang bersifat kontravarian (IComparer<T>).
Ketika objek baru Circle
ditambahkan ke SortedSet<Circle>
, IComparer<Shape>.Compare
metode (IComparer(Of Shape).Compare
metode di Visual Basic) objek ShapeAreaComparer
dipanggil setiap kali elemen baru dibandingkan dengan elemen yang ada. Jenis parameter metode (Shape
) kurang berasal dari jenis yang sedang diteruskan (Circle
), sehingga panggilan berjenis aman. Kontravarian memungkinkan ShapeAreaComparer
untuk mengurutkan koleksi jenis tunggal apa pun, serta koleksi campuran jenis, yang berasal dari 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
Delegasi umum dengan parameter tipe yang bervariasi
Delegasi Func
generik, seperti Func<T,TResult>, memiliki jenis pengembalian kovarian dan jenis parameter kontravarian. Delegasi Action
generik, seperti Action<T1,T2>, memiliki jenis parameter yang kontravarian. Ini berarti bahwa delegasi dapat ditetapkan ke variabel yang memiliki lebih banyak jenis parameter turunan Func
dan (dalam kasus delegasi generik) jenis pengembalian yang kurang diturunkan.
Nota
Parameter jenis generik terakhir dari Func
delegasi generik menentukan jenis nilai yang dikembalikan dalam tanda tangan delegasi. Ini kovarian (out
kata kunci), sedangkan parameter jenis generik lainnya kontravarian (in
kata kunci).
Kode berikut mengilustrasikan hal ini. Bagian pertama kode mendefinisikan kelas bernama Base
, kelas bernama Derived
yang mewarisi Base
, dan kelas lain dengan static
metode (Shared
di Visual Basic) bernama MyMethod
. Metode ini mengambil instans Base
dan mengembalikan instans Derived
. (Jika argumen adalah instans Derived
, MyMethod
mengembalikannya; jika argumen adalah instans Base
, MyMethod
mengembalikan instans baru Derived
.) Dalam Main()
, contoh membuat instans Func<Base, Derived>
(Func(Of Base, Derived)
di Visual Basic) yang mewakili MyMethod
, dan menyimpannya dalam variabel 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
Bagian kedua dari kode menunjukkan bahwa delegate dapat ditetapkan ke variabel dengan tipe Func<Base, Base>
(Func(Of Base, Base)
di Visual Basic), karena tipe pengembalian bersifat kovarian.
// 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())
Bagian ketiga kode menunjukkan bahwa delegasi dapat ditetapkan ke variabel jenis Func<Derived, Derived>
(Func(Of Derived, Derived)
di Visual Basic), karena jenis parameter kontravarian.
// 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())
Bagian akhir kode menunjukkan bahwa delegasi dapat ditetapkan ke variabel dengan jenis Func<Derived, Base>
(Func(Of Derived, Base)
di Visual Basic), yang menggabungkan efek tipe parameter kontravarian dan tipe pengembalian kovarian.
// 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())
Variasi pada delegat non-generik
Dalam kode sebelumnya, tanda tangan MyMethod
sama persis dengan tanda tangan delegasi generik yang dibangun: Func<Base, Derived>
(Func(Of Base, Derived)
di Visual Basic). Contoh menunjukkan bahwa delegasi generik ini dapat disimpan dalam variabel atau parameter metode yang memiliki jenis parameter yang lebih turunan dan jenis pengembalian yang kurang diturunkan, selama semua jenis delegasi dibangun dari jenis Func<T,TResult>delegasi generik .
Ini adalah poin penting. Efek kovariansi dan kontravariansi dalam parameter jenis delegasi generik mirip dengan efek kovarians dan kontravariansi dalam pengikatan delegasi biasa (lihat Varians dalam Delegasi (C#) dan Variansi dalam Delegasi (Visual Basic)). Namun, varians dalam pengikatan delegasi berfungsi dengan semua jenis delegasi, bukan hanya dengan jenis delegasi generik yang memiliki parameter jenis varian. Selain itu, variansi dalam pengikatan delegasi memungkinkan suatu metode terikat kepada delegasi apa pun yang memiliki jenis parameter lebih ketat dan jenis pengembalian yang kurang ketat, sedangkan penetapan delegasi generik hanya berfungsi jika kedua jenis delegasi dibangun dari definisi tipe generik yang sama.
Contoh berikut menunjukkan efek gabungan variasi dalam pengikatan delegasi dan variasi dalam parameter tipe generik. Contoh mendefinisikan hierarki jenis yang mencakup tiga jenis, dari yang paling tidak diturunkan (Type1
) hingga yang paling turunan (Type3
). Variasi dalam pengikatan delegasi biasa digunakan untuk mengikat metode dengan jenis parameter Type1
dan jenis pengembalian Type3
ke delegasi generik dengan jenis parameter Type2
dan jenis pengembalian Type2
. Delegasi generik yang dihasilkan kemudian ditetapkan ke variabel lain yang jenis delegasi generiknya memiliki parameter dengan jenis Type3
dan jenis pengembalian Type1
, menggunakan kovariansi dan kontravariansi parameter jenis generik. Penetapan kedua memerlukan jenis variabel dan jenis delegasi untuk dibangun dari definisi jenis generik yang sama, dalam hal ini, 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
Menentukan antarmuka generik varian dan delegasi
Visual Basic dan C# memiliki kata kunci yang memungkinkan Anda menandai parameter jenis generik antarmuka dan delegasi sebagai kovarian atau kontravarian.
Parameter jenis kovarian ditandai dengan out
kata kunci (Out
kata kunci di Visual Basic). Anda dapat menggunakan parameter jenis kovarian sebagai nilai pengembalian metode milik antarmuka, atau sebagai jenis pengembalian delegasi. Anda tidak dapat menggunakan parameter jenis kovarian sebagai batasan jenis generik untuk metode antarmuka.
Nota
Jika metode antarmuka memiliki parameter yang merupakan jenis delegasi generik, parameter jenis kovarian dari jenis antarmuka dapat digunakan untuk menentukan parameter jenis kontravarian dari jenis delegasi.
Parameter jenis kontravarian ditandai dengan in
kata kunci (In
kata kunci di Visual Basic). Anda dapat menggunakan parameter jenis kontravarian sebagai jenis parameter metode milik antarmuka, atau sebagai jenis parameter delegasi. Anda dapat menggunakan parameter jenis kontravarian sebagai batasan jenis generik untuk metode antarmuka.
Hanya jenis antarmuka dan jenis delegasi yang dapat memiliki parameter jenis varian. Jenis antarmuka atau delegasi dapat memiliki parameter jenis kovarian dan kontravarian.
Visual Basic dan C# tidak memungkinkan Anda melanggar aturan untuk menggunakan parameter jenis kovarian dan kontravarian, atau menambahkan anotasi kovarian dan kontravarian ke parameter jenis selain antarmuka dan delegasi.
Untuk informasi dan contoh kode, lihat Varians dalam Antarmuka Generik (C#) dan Varians di Antarmuka Generik (Visual Basic).
Daftar tipe
Jenis antarmuka dan delegasi berikut memiliki parameter jenis kovarian dan/atau kontravarian.
Tipe | Parameter tipe kovarian | Parameter jenis kontravarian |
---|---|---|
Action<T> ke Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16> | Ya | |
Comparison<T> | Ya | |
Converter<TInput,TOutput> | Ya | Ya |
Func<TResult> | Ya | |
Func<T,TResult> ke Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult> | Ya | Ya |
IComparable<T> | Ya | |
Predicate<T> | Ya | |
IComparer<T> | Ya | |
IEnumerable<T> | Ya | |
IEnumerator<T> | Ya | |
IEqualityComparer<T> | Ya | |
IGrouping<TKey,TElement> | Ya | |
IOrderedEnumerable<TElement> | Ya | |
IOrderedQueryable<T> | Ya | |
IQueryable<T> | Ya |