<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.