Karena string mewarisi object, wajar kita berharap string[] dapat dipandang sebagai object[]. Harapan itu benar dan disebut covariance: konversi mengikuti arah pewarisan tipe. Arah sebaliknya juga punya nama, contravariance, dan berguna untuk parameter method. Bab ini membahas variance pada array dan pengantar variance pada tipe generic IEnumerable<T>, Func<T>, dan Action<T>; pembahasan mendalam tersedia pada section Generics.

Covariance pada Array #

Array di C# bersifat covariant terhadap tipe elemennya, sehingga penugasan object[] kotak = daftarString sah menurut compiler. Namun, array menyimpan elemen dan menerima penulisan, sehingga keamanan tipe tidak bisa dijamin penuh pada waktu kompilasi. Runtime menutup celah itu dengan pemeriksaan saat penulisan elemen: bila nilai yang ditulis tidak sesuai tipe asli array, runtime melempar ArrayTypeMismatchException.

Contoh: Array dan Pemeriksaan Runtime #

csharp
string[] daftarBurung = { "Merpati", "Beo" };

object[] kotakBurung = daftarBurung;
Console.WriteLine(kotakBurung[0]);

try
{
    kotakBurung[1] = 42;
}
catch (ArrayTypeMismatchException ex)
{
    Console.WriteLine(ex.GetType().Name);
}

Output yang Dihasilkan:

text
Merpati
ArrayTypeMismatchException

Mari kita bedah baris demi baris:

  1. object[] kotakBurung = daftarBurung;: Array covariance mengizinkan penugasan ini karena string mewarisi object; kedua variable menunjuk array yang sama.
  2. Console.WriteLine(kotakBurung[0]): Pembacaan lewat tipe object[] aman, karena setiap string memang adalah object; output menampilkan Merpati.
  3. kotakBurung[1] = 42;: Nilai int bukan string, dan array yang ditunjuk tetap array string; compiler melewati baris ini, tetapi runtime menolaknya.
  4. catch (ArrayTypeMismatchException ex): Penangkap exception mencetak nama tipenya, membuktikan pemeriksaan array terjadi pada waktu eksekusi, bukan waktu kompilasi.

Variance pada Tipe Generic #

Tipe generic tertentu mendeklarasikan arah variance-nya secara eksplisit: parameter tipe out T berarti covariant, sedangkan in T berarti contravariant.

Parameter tipeArahContoh tipeKonversi yang sah
out TCovariantIEnumerable<out T>, Func<out TResult>IEnumerable<string> menjadi IEnumerable<object>
in TContravariantAction<in T>Action<object> menjadi Action<string>

Intuisinya: tipe out hanya mengeluarkan nilai, sehingga pengeluaran string aman dipandang sebagai pengeluaran object; tipe in hanya menerima masukan, sehingga penerima object sanggup menerima string apa pun.

csharp
string[] daftarBurung = { "Merpati", "Beo" };

IEnumerable<object> bendaBurung = daftarBurung;
foreach (object benda in bendaBurung)
{
    Console.WriteLine(benda);
}

Func<string> pembuatPesan = () => "Pesan siap dikirim.";
Func<object> pembuatObjek = pembuatPesan;
Console.WriteLine(pembuatObjek());

Action<object> cetakObjek = isi => Console.WriteLine(isi);
Action<string> cetakTeks = cetakObjek;
cetakTeks("Paket nomor 17 sudah diproses.");

Output yang Dihasilkan:

text
Merpati
Beo
Pesan siap dikirim.
Paket nomor 17 sudah diproses.

Baris IEnumerable<object> bendaBurung = daftarBurung; memakai covariance karena T bertanda out; baris Action<string> cetakTeks = cetakObjek; memakai contravariance karena T bertanda in. Ketiga penugasan generic pada contoh lulus kompilasi tanpa peringatan, berbeda dari array yang menunggu pemeriksaan runtime.

Bab berikutnya menggabungkan seluruh mekanisme inheritance menjadi satu teknik inti: runtime polymorphism.