<out T> vs <T> ใน Generics


189

อะไรคือความแตกต่างระหว่าง <out T>และ<T>คืออะไร? ตัวอย่างเช่น:

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

เมื่อเทียบกับ

public interface IExample<T>
{
    ...
}

1
ตัวอย่างที่ดีคือ IObservable <T> และ IObserver <T> ซึ่งกำหนดไว้ในระบบ ns ใน mscorlib ส่วนต่อประสานสาธารณะ IObservable <out T> และส่วนต่อประสานสาธารณะ IObserver <ใน T> ในทำนองเดียวกัน IEnumerator <out T>, IEnumerable <out T>
VivekDev

2
คำอธิบายที่ดีที่สุดที่ฉันพบ: agirlamonggeeks.com/2019/05/29/… . (<ใน T> <- หมายความว่า T สามารถส่งผ่านเป็นพารามิเตอร์ไปยังเมธอดเท่านั้น <out T> <- หมายความว่า T สามารถเป็น ส่งกลับเฉพาะผลลัพธ์ของเมธอด)
Uladzimir Sharyi

คำตอบ:


212

outคำหลักใน generics ใช้เพื่อแสดงว่า T ประเภทในอินเตอร์เฟซเป็น covariant ดูความแปรปรวนร่วมและความแปรปรวนสำหรับรายละเอียด

IEnumerable<out T>ตัวอย่างคลาสสิกเป็น เนื่องจากIEnumerable<out T>เป็น covariant คุณได้รับอนุญาตให้ทำสิ่งต่อไปนี้:

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

บรรทัดที่สองด้านบนจะล้มเหลวหากนี่ไม่ใช่ค่าแปรปรวนแม้ว่ามันควรจะทำงานได้ดีเนื่องจากสตริงเกิดขึ้นจากวัตถุ ก่อนความแปรปรวนในอินเทอร์เฟซทั่วไปให้กับ C # และ VB.NET (ใน. NET 4 ที่มี VS 2010) นี่เป็นข้อผิดพลาดเวลาในการรวบรวม

หลังจาก .NET 4 IEnumerable<T>ที่ถูกทำเครื่องหมาย covariant IEnumerable<out T>และกลายเป็น เนื่องจากIEnumerable<out T>ใช้องค์ประกอบภายในเท่านั้นและไม่เคยเพิ่ม / เปลี่ยนแปลงมันจึงปลอดภัยที่จะปฏิบัติต่อคอลเลกชันที่นับได้ของสตริงเป็นคอลเลกชันของวัตถุที่นับไม่ได้ซึ่งหมายความว่ามันแปรปรวนร่วม covariant

สิ่งนี้จะไม่ทำงานกับประเภทที่คล้ายIList<T>กันเนื่องจากIList<T>มีAddวิธีการ สมมติว่าสิ่งนี้จะได้รับอนุญาต:

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

จากนั้นคุณสามารถโทร:

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

แน่นอนว่าสิ่งนี้จะล้มเหลว - ดังนั้นจึงIList<T>ไม่สามารถทำเครื่องหมายโควาเรียนต์ได้

นอกจากนี้ยังมี btw ตัวเลือกสำหรับin- ซึ่งถูกใช้โดยสิ่งต่าง ๆ เช่นอินเทอร์เฟซการเปรียบเทียบ IComparer<in T>ตัวอย่างเช่นทำงานในทางตรงกันข้าม คุณสามารถใช้คอนกรีตIComparer<Foo>โดยตรงเป็นIComparer<Bar>ถ้าBarเป็น subclass ของFooเพราะIComparer<in T>อินเตอร์เฟซที่contravariant


4
@ColeJohnson เพราะImageเป็นคลาสนามธรรม;) คุณสามารถทำได้new List<object>() { Image.FromFile("test.jpg") };โดยไม่มีปัญหาหรือคุณสามารถทำได้new List<object>() { new Bitmap("test.jpg") };เช่นกัน ปัญหาของคุณคือnew Image()ไม่ได้รับอนุญาต (คุณทำไม่ได้var img = new Image();)
Reed Copsey

4
ทั่วไปIList<object>เป็นตัวอย่างที่แปลกประหลาดถ้าคุณต้องการobjects คุณไม่จำเป็นต้องใช้ยาชื่อสามัญ
Jodrell

5
@ReedCopsey คุณไม่ได้ขัดแย้งกับคำตอบของคุณเองในความคิดเห็นของคุณ?
MarioDS

64

สำหรับการจดจำการใช้งานinและoutคำหลักได้อย่างง่ายดาย(เช่นความแปรปรวนร่วมและความแปรปรวน) เราสามารถจำลองการสืบทอดเป็นภาพ:

String : Object
Bar : Foo

เข้า / ออก


11
นี่ไม่ใช่วิธีที่ผิดใช่มั้ย Contravariance = in = อนุญาตให้ใช้ชนิดที่ได้รับน้อยลงแทนการได้รับมากกว่า / Covariance = out = อนุญาตให้ใช้ประเภทที่ได้รับเพิ่มเติมแทนการได้รับน้อยกว่า โดยส่วนตัวเมื่อดูที่แผนภาพของคุณฉันอ่านมันตรงข้ามกับสิ่งนั้น
Sam Shiles

ร่วมUตัวแปร (: สำหรับฉัน
SNR

49

พิจารณา,

class Fruit {}

class Banana : Fruit {}

interface ICovariantSkinned<out T> {}

interface ISkinned<T> {}

และฟังก์ชั่น

void Peel(ISkinned<Fruit> skinned) { }

void Peel(ICovariantSkinned<Fruit> skinned) { }

ฟังก์ชั่นที่ยอมรับICovariantSkinned<Fruit>จะสามารถยอมรับICovariantSkinned<Fruit>หรือICovariantSkinned<Bananna>เพราะICovariantSkinned<T>เป็นอินเตอร์เฟซ covariant และBananaเป็นประเภทของFruit ,

ฟังก์ชั่นที่ยอมรับISkinned<Fruit>จะสามารถยอมรับISkinned<Fruit>ได้เท่านั้น


38

" out T" หมายถึงประเภทTนั้นคือ "covariant" ที่ จำกัดTให้ปรากฏเฉพาะเป็นค่าที่ส่งคืน (ขาออก) ในวิธีการของคลาสทั่วไปอินเทอร์เฟซหรือวิธีการ ความหมายคือที่คุณสามารถโยนประเภท / อินเตอร์เฟซ / Tวิธีการเพื่อให้เทียบเท่ากับซุปเปอร์ประเภทของ
เช่นสามารถที่จะโยนICovariant<out Dog>ICovariant<Animal>


14
ฉันไม่ได้ตระหนักว่าการoutบังคับใช้ที่Tสามารถส่งคืนได้เท่านั้นจนกว่าฉันจะอ่านคำตอบนี้ แนวคิดทั้งหมดนี้สมเหตุสมผลมากขึ้นในขณะนี้!
MarioDS

6

จากลิงค์ที่คุณโพสต์ ....

สำหรับพารามิเตอร์ประเภททั่วไป, ออกระบุคำหลักที่พารามิเตอร์ชนิดคือ covariant

แก้ไข : อีกครั้งจากลิงค์ที่คุณโพสต์

สำหรับข้อมูลเพิ่มเติมดูความแปรปรวนร่วมและความแปรปรวน (C # และ Visual Basic) http://msdn.microsoft.com/en-us/library/ee207183.aspx

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.