<out T> vs <T> trong Generics


188

Sự khác biệt giữa <out T>và là <T>gì? Ví dụ:

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

so với

public interface IExample<T>
{
    ...
}

1
Ví dụ điển hình sẽ là IObservable <T> và IObserver <T>, được định nghĩa trong ns hệ thống trong mscorlib. giao diện công cộng IObservable <out T> và giao diện công cộng IObserver <trong T>. Tương tự, IEnumerator <out T>, IEnumerable <out T>
VivekDev 6/2/2016

1
Giải thích tốt nhất mà tôi đã gặp: agirlamonggeek.com/2019/05/29/ . (< In T> <- có nghĩa là T chỉ có thể được truyền dưới dạng tham số cho một phương thức; chỉ được trả về dưới dạng kết quả của phương thức)
Uladzimir Sharyi

Câu trả lời:


210

Các outtừ khóa trong Generics được sử dụng để biểu thị rằng các loại T trong giao diện là hiệp biến. Xem hiệp phương sai và chống chỉ định để biết chi tiết.

Ví dụ kinh điển là IEnumerable<out T>. Vì IEnumerable<out T>là covariant, bạn được phép làm như sau:

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

Dòng thứ hai ở trên sẽ thất bại nếu điều này không đồng biến, mặc dù về mặt logic nó sẽ hoạt động, vì chuỗi xuất phát từ đối tượng. Trước khi phương sai trong các giao diện chung được thêm vào C # và VB.NET (trong .NET 4 với VS 2010), đây là lỗi thời gian biên dịch.

Sau .NET 4, IEnumerable<T>được đánh dấu covariant và trở thành IEnumerable<out T>. Vì IEnumerable<out T>chỉ sử dụng các phần tử bên trong nó và không bao giờ thêm / thay đổi chúng, nên nó an toàn để coi một bộ sưu tập chuỗi vô số như một bộ sưu tập các đối tượng, có nghĩa là nó là đồng biến .

Điều này sẽ không hoạt động với một loại như IList<T>, vì IList<T>có một Addphương pháp. Giả sử điều này sẽ được cho phép:

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

Sau đó bạn có thể gọi:

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

Tất nhiên, điều này sẽ thất bại - vì vậy IList<T>không thể đánh dấu covariant.

Ngoài ra còn có, btw, một tùy chọn cho in- được sử dụng bởi những thứ như giao diện so sánh. IComparer<in T>, ví dụ, hoạt động theo cách ngược lại. Bạn có thể sử dụng một bê tông IComparer<Foo>trực tiếp như một IComparer<Bar>nếu Barlà một lớp con của Foo, bởi vì IComparer<in T>giao diện là contravariant .


4
@ColeJohnson Bởi vì Imagelà một lớp trừu tượng;) Bạn có thể làm new List<object>() { Image.FromFile("test.jpg") };mà không có vấn đề gì, hoặc bạn cũng có thể làm new List<object>() { new Bitmap("test.jpg") };như vậy. Vấn đề với bạn là điều đó new Image()không được phép (bạn cũng không thể làm var img = new Image();được)
Reed Copsey

4
một cái chung IList<object>là một ví dụ kỳ quái, nếu bạn muốn objectbạn không cần thuốc generic.
Jodrell

5
@ReedCopsey Bạn không mâu thuẫn với câu trả lời của chính mình trong bình luận của bạn?
MarioDS

64

Để dễ dàng ghi nhớ việc sử dụng inouttừ khóa (cũng là hiệp phương sai và chống đối), chúng ta có thể hình ảnh kế thừa như gói:

String : Object
Bar : Foo

vào / ra


11
Đây không phải là cách sai? Contravariance = in = cho phép các loại dẫn xuất ít hơn được sử dụng thay cho các dẫn xuất nhiều hơn. / Covariance = out = cho phép sử dụng nhiều loại dẫn xuất hơn thay cho ít dẫn xuất hơn. Cá nhân, nhìn vào sơ đồ của bạn, tôi đọc nó như đối nghịch với điều đó.
Sam Shiles

co u biến thể (: cho tôi
snr

49

xem xét,

class Fruit {}

class Banana : Fruit {}

interface ICovariantSkinned<out T> {}

interface ISkinned<T> {}

và các chức năng,

void Peel(ISkinned<Fruit> skinned) { }

void Peel(ICovariantSkinned<Fruit> skinned) { }

Hàm chấp nhận ICovariantSkinned<Fruit>sẽ có thể chấp nhận ICovariantSkinned<Fruit>hoặc ICovariantSkinned<Bananna>ICovariantSkinned<T>là giao diện covariant và Bananalà một loại Fruit,

chức năng chấp nhận ISkinned<Fruit>sẽ chỉ có thể chấp nhận ISkinned<Fruit>.


37

" out T" có nghĩa là loại đó Tlà "covariant". Điều đó hạn chế Tchỉ xuất hiện dưới dạng giá trị trả về (bên ngoài) trong các phương thức của lớp, giao diện hoặc phương thức chung. Hàm ý là bạn có thể truyền kiểu / giao diện / phương thức tương đương với siêu kiểu T.
Ví dụ ICovariant<out Dog>có thể được đúc ICovariant<Animal>.


13
Tôi đã không nhận ra rằng các outthực thi chỉ Tcó thể được trả lại, cho đến khi tôi đọc câu trả lời này. Toàn bộ khái niệm có ý nghĩa hơn bây giờ!
MarioDS

6

Từ liên kết bạn đã đăng ....

Đối với các tham số loại chung, từ khóa out chỉ định rằng tham số loại là covariant .

EDIT : Một lần nữa, từ liên kết bạn đã đăng

Để biết thêm thông tin, hãy xem Hiệp phương sai và Chống chỉ định (C # và Visual Basic). http://msdn.microsoft.com/en-us/l Library / ee207183.aspx

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.