Saya telah menemukan ini di buku teks yang saya baca di C #, tetapi saya mengalami kesulitan untuk memahaminya, mungkin karena kurangnya konteks.
Apakah ada penjelasan singkat yang bagus tentang apa itu dan apa gunanya di luar sana?
Edit untuk klarifikasi:
Antarmuka kovarian:
interface IBibble<out T>
.
.
Antarmuka kontravarian:
interface IBibble<in T>
.
.
c#
.net
interface
covariance
contravariance
NibblyPig
sumber
sumber
Jawaban:
Dengan
<out T>
, Anda bisa memperlakukan referensi antarmuka sebagai referensi ke atas dalam hierarki.Dengan
<in T>
, Anda dapat memperlakukan referensi antarmuka sebagai referensi ke bawah di hiearchy.Izinkan saya mencoba menjelaskannya dalam istilah yang lebih bahasa Inggris.
Katakanlah Anda mengambil daftar hewan dari kebun binatang, dan Anda berniat untuk memprosesnya. Semua hewan (di kebun binatang Anda) memiliki nama, dan ID unik. Beberapa hewan adalah mamalia, beberapa reptil, beberapa amfibi, beberapa ikan, dll. Tetapi semuanya adalah hewan.
Jadi, dengan daftar hewan Anda (berisi hewan dari berbagai jenis), Anda dapat mengatakan bahwa semua hewan memiliki nama, jadi jelas akan aman untuk mendapatkan nama semua hewan.
Namun, bagaimana jika Anda hanya memiliki daftar ikan, tetapi perlu memperlakukannya seperti hewan, apakah itu berhasil? Secara intuitif, ini seharusnya berfungsi, tetapi di C # 3.0 dan sebelumnya, bagian kode ini tidak akan dikompilasi:
IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish>
Alasan untuk ini adalah bahwa kompilator tidak "tahu" apa yang Anda inginkan, atau bisa , lakukan dengan koleksi binatang setelah Anda mengambilnya. Untuk semua yang diketahui, mungkin ada jalan masuk
IEnumerable<T>
untuk memasukkan kembali objek ke dalam daftar, dan itu berpotensi memungkinkan Anda untuk memasukkan hewan yang bukan ikan, ke dalam koleksi yang seharusnya hanya berisi ikan.Dengan kata lain, kompilator tidak dapat menjamin bahwa ini tidak diperbolehkan:
animals.Add(new Mammal("Zebra"));
Jadi kompilator langsung menolak untuk mengkompilasi kode Anda. Ini adalah kovarians.
Mari kita lihat pelanggaran.
Karena kebun binatang kita bisa menangani semua hewan, pasti bisa menangani ikan, jadi mari kita coba menambahkan beberapa ikan ke kebun binatang kita.
Di C # 3.0 dan sebelumnya, ini tidak dapat dikompilasi:
List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal> fishes.Add(new Fish("Guppy"));
Di sini, kompiler dapat mengizinkan potongan kode ini, meskipun metode tersebut kembali
List<Animal>
hanya karena semua ikan adalah hewan, jadi jika kita mengubah tipenya menjadi ini:List<Animal> fishes = GetAccessToFishes(); fishes.Add(new Fish("Guppy"));
Maka itu akan berhasil, tetapi kompilator tidak dapat menentukan bahwa Anda tidak mencoba melakukan ini:
List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal> Fish firstFist = fishes[0];
Karena daftar tersebut sebenarnya adalah daftar hewan, hal ini tidak diperbolehkan.
Jadi kontra dan kovarian adalah bagaimana Anda memperlakukan referensi objek dan apa yang boleh Anda lakukan dengannya.
Kata kunci
in
danout
dalam C # 4.0 secara khusus menandai antarmuka sebagai satu atau yang lain. Denganin
, Anda diizinkan untuk menempatkan tipe generik (biasanya T) di input -positions, yang berarti argumen metode, dan properti hanya-tulis.Dengan
out
, Anda diizinkan untuk menempatkan tipe generik dalam output -positions, yang merupakan nilai kembalian metode, properti hanya-baca, dan parameter metode keluar.Ini akan memungkinkan Anda melakukan apa yang ingin dilakukan dengan kode:
IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish> // since we can only get animals *out* of the collection, every fish is an animal // so this is safe
List<T>
memiliki arah masuk dan keluar di T, jadi ini bukan varian bersama atau kontra-varian, tetapi antarmuka yang memungkinkan Anda menambahkan objek, seperti ini:interface IWriteOnlyList<in T> { void Add(T value); }
akan memungkinkan Anda melakukan ini:
IWriteOnlyList<Fish> fishes = GetWriteAccessToAnimals(); // still returns IWriteOnlyList<Animal> fishes.Add(new Fish("Guppy")); <-- this is now safe
Berikut beberapa video yang menunjukkan konsep tersebut:
Berikut contohnya:
namespace SO2719954 { class Base { } class Descendant : Base { } interface IBibbleOut<out T> { } interface IBibbleIn<in T> { } class Program { static void Main(string[] args) { // We can do this since every Descendant is also a Base // and there is no chance we can put Base objects into // the returned object, since T is "out" // We can not, however, put Base objects into b, since all // Base objects might not be Descendant. IBibbleOut<Base> b = GetOutDescendant(); // We can do this since every Descendant is also a Base // and we can now put Descendant objects into Base // We can not, however, retrieve Descendant objects out // of d, since all Base objects might not be Descendant IBibbleIn<Descendant> d = GetInBase(); } static IBibbleOut<Descendant> GetOutDescendant() { return null; } static IBibbleIn<Base> GetInBase() { return null; } } }
Tanpa tanda ini, berikut ini dapat dikompilasi:
public List<Descendant> GetDescendants() ... List<Base> bases = GetDescendants(); bases.Add(new Base()); <-- uh-oh, we try to add a Base to a Descendant
atau ini:
public List<Base> GetBases() ... List<Descendant> descendants = GetBases(); <-- uh-oh, we try to treat all Bases as Descendants
sumber
Posting ini adalah yang terbaik yang pernah saya baca tentang masalah ini
Singkatnya, kovarian / kontravarian / invarian berhubungan dengan pengecoran tipe otomatis (dari basis ke turunan dan sebaliknya). Jenis cetakan tersebut hanya mungkin jika beberapa jaminan dipatuhi dalam hal tindakan baca / tulis yang dilakukan pada objek yang dicor. Baca postingan untuk lebih jelasnya.
sumber