<out T> vs <T> dalam Generics


189

Apa perbedaan antara <out T>dan <T>? Sebagai contoh:

public interface IExample<out T>
{
    ...
}

vs.

public interface IExample<T>
{
    ...
}

1
Contoh yang bagus adalah IObservable <T> dan IObserver <T>, didefinisikan dalam sistem ns di mscorlib. antarmuka publik IObservable <out T>, dan antarmuka publik IObserver <dalam T>. Demikian pula, IEnumerator <out T>, IEnumerable <out T>
VivekDev

2
Penjelasan terbaik yang saya temui: agirlamonggeeks.com/2019/05/29/… . (<Dalam T> <- berarti bahwa T hanya dapat diteruskan sebagai parameter ke suatu metode; <out T> <- berarti bahwa T dapat menjadi hanya dikembalikan sebagai hasil metode)
Uladzimir Sharyi

Jawaban:


212

Kata outkunci dalam generik digunakan untuk menunjukkan bahwa tipe T di antarmuka adalah kovarian. Lihat Covariance dan contravariance untuk detailnya.

Contoh klasiknya adalah IEnumerable<out T>. Karena IEnumerable<out T>kovarian, Anda diperbolehkan melakukan hal berikut:

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;

Baris kedua di atas akan gagal jika ini bukan kovarian, meskipun secara logis seharusnya bekerja, karena string berasal dari objek. Sebelum variasi dalam antarmuka umum ditambahkan ke C # dan VB.NET (dalam .NET 4 dengan VS 2010), ini adalah kesalahan waktu kompilasi.

Setelah .NET 4, IEnumerable<T>ditandai kovarian, dan menjadi IEnumerable<out T>. Karena IEnumerable<out T>hanya menggunakan elemen di dalamnya, dan tidak pernah menambahkan / mengubahnya, aman untuk memperlakukan kumpulan string yang dapat dihitung sebagai kumpulan objek yang dapat dihitung, yang berarti kovarian .

Ini tidak akan bekerja dengan tipe seperti IList<T>, karena IList<T>memiliki Addmetode. Misalkan ini diizinkan:

IList<string> strings = new List<string>();
IList<object> objects = strings;  // NOTE: Fails at compile time

Anda kemudian dapat menelepon:

objects.Add(new Image()); // This should work, since IList<object> should let us add **any** object

Ini tentu saja akan gagal - jadi IList<T>tidak dapat ditandai kovarian.

Ada juga, btw, opsi untuk in- yang digunakan oleh hal-hal seperti antarmuka perbandingan. IComparer<in T>, misalnya, bekerja dengan cara yang berlawanan. Anda dapat menggunakan beton IComparer<Foo>secara langsung sebagai IComparer<Bar>if Baradalah subclass Foo, karena IComparer<in T>antarmuka bersifat contravariant .


4
@ColeJohnson Karena Imageadalah kelas abstrak;) Anda dapat melakukannya new List<object>() { Image.FromFile("test.jpg") };tanpa masalah, atau Anda dapat melakukannya new List<object>() { new Bitmap("test.jpg") };juga. Masalah dengan Anda adalah yang new Image()tidak diizinkan (Anda tidak dapat melakukan var img = new Image();keduanya)
Reed Copsey

4
generik IList<object>adalah contoh aneh, jika Anda mau object, Anda tidak perlu obat generik.
Jodrell

5
@ReedCopsey Apakah Anda tidak menentang jawaban Anda sendiri dalam komentar Anda?
MarioDS

64

Untuk mengingat dengan mudah penggunaan indan outkata kunci (juga kovarian dan contravariance), kita dapat gambar pewarisan sebagai pembungkus:

String : Object
Bar : Foo

masuk / keluar


11
Bukankah ini cara yang salah? Contravariance = in = memungkinkan jenis turunan yang lebih sedikit digunakan sebagai pengganti turunan yang lebih banyak. / Covariance = out = memungkinkan lebih banyak tipe turunan digunakan sebagai pengganti turunan yang kurang. Secara pribadi, melihat diagram Anda, saya membacanya sebagai kebalikan dari itu.
Sam Shiles

co u varian (: bagi saya
SNR

49

mempertimbangkan,

class Fruit {}

class Banana : Fruit {}

interface ICovariantSkinned<out T> {}

interface ISkinned<T> {}

dan fungsinya,

void Peel(ISkinned<Fruit> skinned) { }

void Peel(ICovariantSkinned<Fruit> skinned) { }

Fungsi yang menerima ICovariantSkinned<Fruit>akan dapat menerima ICovariantSkinned<Fruit>atau ICovariantSkinned<Bananna>karena ICovariantSkinned<T>merupakan antarmuka kovarian dan Bananamerupakan jenis Fruit,

fungsi yang menerima ISkinned<Fruit>hanya akan dapat menerima ISkinned<Fruit>.


38

" out T" berarti tipe Titu "kovarian". Itu membatasi Tmuncul hanya sebagai nilai yang dikembalikan (keluar) dalam metode kelas generik, antarmuka atau metode. Implikasinya adalah Anda dapat menggunakan tipe / antarmuka / metode yang setara dengan tipe super T.
Misalnya ICovariant<out Dog>dapat dilemparkan ke ICovariant<Animal>.


14
Saya tidak menyadari bahwa outpenegakan yang Tdapat dikembalikan hanya, sampai saya membaca jawaban ini. Seluruh konsep lebih masuk akal sekarang!
MarioDS

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.