Generic Covariance & Contra-variance ดำเนินการอย่างไรใน C # 4.0


106

ฉันไม่ได้เข้าร่วม PDC 2008 แต่ฉันได้ยินข่าวมาว่า C # 4.0 ได้รับการประกาศเพื่อรองรับความแปรปรวนร่วมทั่วไปและความแปรปรวนตรงกันข้าม นั่นคือสามารถกำหนดให้List<string> List<object>เป็นไปได้อย่างไร?

ในหนังสือC # in Depthของ Jon Skeet อธิบายว่าทำไม C # generics ไม่สนับสนุนความแปรปรวนร่วมและความแปรปรวนตรงกันข้าม ส่วนใหญ่เป็นการเขียนโค้ดที่ปลอดภัย ตอนนี้ C # 4.0 เปลี่ยนไปรองรับพวกเขาแล้ว มันจะนำมาซึ่งความสับสนวุ่นวาย?

ใครทราบรายละเอียดเกี่ยวกับ C # 4.0 ช่วยให้คำอธิบายได้บ้าง?


นี่คือบทความดีๆที่ครอบคลุมการใช้งานความแปรปรวนร่วมและความแปรปรวนที่กำลังจะเกิดขึ้นกับตัวแทนและอินเทอร์เฟซใน C # 4.0: LINQ Farm: Covariance and Contravariance ใน C # 4.0
CMS

Anders Noråseอธิบายไว้ในC # 4.0 -แนวคิดและการแสดงความแปรปรวนร่วมและตรงกันข้ามซึ่งได้รับการสนับสนุนแล้วใน IL ตั้งแต่. NET 2.0
Thomas Freudenberg

คำตอบ:


155

ความแปรปรวนจะได้รับการสนับสนุนในวิธีที่ปลอดภัยเท่านั้น - ในความเป็นจริงโดยใช้ความสามารถที่ CLR มีอยู่แล้ว ดังนั้นตัวอย่างที่ผมให้ไว้ในหนังสือพยายามที่จะใช้ที่List<Banana>เป็นList<Fruit>(หรือสิ่งที่มันเป็น) ยังจะไม่ทำงาน - แต่สถานการณ์อื่น ๆ ไม่กี่จะ

ประการแรกจะรองรับเฉพาะอินเทอร์เฟซและผู้รับมอบสิทธิ์เท่านั้น

ประการที่สองต้องมีผู้สร้างอินเทอร์เฟซ / ผู้รับมอบสิทธิ์ในการตกแต่งพารามิเตอร์ประเภทเป็นin(สำหรับความแตกต่าง) หรือout(สำหรับความแปรปรวนร่วม) ตัวอย่างที่ชัดเจนที่สุดคือIEnumerable<T>สิ่งที่ให้คุณ "ออก" จากค่านี้เท่านั้น แต่จะไม่ให้คุณเพิ่มค่าใหม่ IEnumerable<out T>ที่จะกลายเป็น นั่นไม่ได้ทำร้ายความปลอดภัยของประเภทเลย แต่ให้คุณส่งคืนIEnumerable<string>จากวิธีการที่ประกาศให้ส่งคืนIEnumerable<object>ตัวอย่างเช่น

ความแตกต่างนั้นยากที่จะยกตัวอย่างที่เป็นรูปธรรมสำหรับการใช้อินเทอร์เฟซ แต่เป็นเรื่องง่ายสำหรับผู้ร่วมประชุม พิจารณาAction<T>- นั่นแสดงถึงวิธีการที่ใช้Tพารามิเตอร์ จะเป็นการดีที่จะสามารถแปลงใช้ an Action<object>เป็น an ได้อย่างราบรื่นAction<string>- วิธีการใด ๆ ที่ใช้objectพารามิเตอร์จะดีเมื่อนำเสนอด้วย a stringแทน แน่นอน C # 2 มีความแปรปรวนร่วมและความแตกต่างของผู้ร่วมประชุมอยู่แล้วในระดับหนึ่ง แต่ผ่านการแปลงจริงจากประเภทผู้รับมอบสิทธิ์หนึ่งไปยังอีกประเภทหนึ่ง (การสร้างอินสแตนซ์ใหม่) - ดูตัวอย่าง P141-144 C # 4 จะทำให้สิ่งนี้กว้างขึ้นและ (ฉันเชื่อว่า) จะหลีกเลี่ยงการสร้างอินสแตนซ์ใหม่สำหรับการแปลง (จะเป็นการแปลงอ้างอิงแทน)

หวังว่าสิ่งนี้จะกระจ่างขึ้นสักหน่อย - โปรดแจ้งให้เราทราบหากไม่สมเหตุสมผล!


3
ดังนั้นหมายความว่าถ้าคลาสถูกประกาศเป็น "List <out T>" ก็ไม่ควรมีฟังก์ชันสมาชิกเช่น "void Add (T obj)"? คอมไพเลอร์ C # 4.0 จะรายงานข้อผิดพลาดนั้นใช่ไหม?
Morgan Cheng

1
มอร์แกน: นั่นคือความเข้าใจของฉันใช่
Jon Skeet

4
อีกครั้งหนึ่งในคำตอบของคุณที่นี่ใน SO ช่วยปรับปรุงโค้ดบางอย่างได้ทันที ขอบคุณ!
ทำเครื่องหมาย

@ อาร์คคุง: ใช่ฉันรู้เรื่องนั้น ดังนั้น "ยังใช้ไม่ได้" ในประโยคเดียวกัน (และฉันก็รู้เหตุผลด้วย)
Jon Skeet

@JonSkeet ถูกต้องหรือไม่ที่ "คุณสามารถใช้ a List<Banana>เป็นIList<Fruit>" ได้อย่างที่ @ Ark-kun กล่าว? ถ้าเป็นเช่นนั้นจะเป็นไปได้อย่างไรแม้ว่าพารามิเตอร์ type ของIList<T>อินเทอร์เฟซจะไม่ถูกกำหนดให้เป็นโควาเรียน (ไม่ใช่out Tแต่เป็นเพียงT)
gehho

5

ไม่ใช่ว่าจอนยังไม่ได้กล่าวถึง แต่นี่คือลิงก์ไปยังบล็อกและวิดีโอจาก Eric Lippert เขาอธิบายได้ดีพร้อมกับตัวอย่าง

https://blogs.msdn.microsoft.com/ericlippert/2007/10/16/covariance-and-contravariance-in-c-part-one/

วิดีโอ:

https://www.youtube.com/watch?v=3MQDrKbzvqU

https://www.youtube.com/watch?v=XRIadQaBYlI

https://www.youtube.com/watch?v=St9d2EDZfrg

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