Як реалізована загальна коваріація та контра-дисперсія в C # 4.0?


106

Я не відвідував PDC 2008, але почув новини про те, що оголошено C # 4.0, що підтримує загальну коваріацію та контра-дисперсію. Тобто List<string>може бути призначений List<object>. Як це могло бути?

У книзі C # в глибині Джона Скіта пояснюється, чому дженерики C # не підтримують коваріації та контра-дисперсії. Це в основному для написання захищеного коду. Тепер 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.
Томас Фрейденберг

Відповіді:


155

Варіантність буде підтримуватися лише безпечним способом - фактично, використовуючи здібності, які CLR вже має. Тож приклади, які я наводжу в книзі про спробу використовувати List<Banana>як List<Fruit>(або що б там не було), все одно не спрацюють - але кілька інших сценаріїв будуть.

По-перше, він підтримуватиметься лише для інтерфейсів та делегатів.

По-друге, він вимагає від автора інтерфейсу / делегата прикрасити параметри типу як in(для протиріччя) або out(для коваріації). Найбільш очевидний приклад - IEnumerable<T>це лише коли-небудь дозволяє вибирати з нього значення "з-під" - це не дозволяє додавати нові. Це стане IEnumerable<out T>. Це взагалі не шкодить безпеці типу, але дозволяє повернути IEnumerable<string>метод, наприклад, оголошений для повернення, IEnumerable<object>наприклад.

Протилежність важче наводити конкретні приклади використання інтерфейсів, але це легко з делегатом. Поміркуйте Action<T>- що просто представляє метод, який приймає Tпараметр. Було б непогано мати можливість конвертувати безперешкодно використовувати Action<object>як Action<string>- будь-який метод, який приймає objectпараметр, буде добре, коли він буде представлений stringзамість нього. Звичайно, у C # 2 вже є певна ступінь коваріації та протиріччя делегатів, але шляхом фактичного перетворення з одного типу делегата в інший (створення нового примірника) - див. Приклади P141-144. C # 4 зробить це більш загальним, і (я вважаю) уникне створення нового екземпляра для конверсії. (Натомість це буде конвертація посилань.)

Сподіваюсь, це трохи вияснить це - будь ласка, повідомте мене, якщо це не має сенсу!


3
Отже, чи означає це, що якщо клас оголошено як "Список <з T>", то він НЕ повинен мати функцію члена на зразок "void Add (T obj)"? Компілятор C # 4.0 повідомить про помилку на цьому, правда?
Морган Ченг

1
Морган: Це, безумовно, моє розуміння, так.
Джон Скіт

4
але знову ж таки, одна з ваших відповідей тут на SO негайно допомогла мені покращити деякий код. Дякую!
Марк

@ Арк-кун: Так, я знаю про це. Отже, "все ще не буде працювати" в одному реченні. (І я знаю також причини.)
Джон Скіт

@JonSkeet Правильно, що "ви можете використовувати лише a List<Banana>як IList<Fruit>", як сказав Арк-кун? Якщо так, то як це можливо, хоча параметр типу IList<T>інтерфейсу не визначається як коваріантний (ні out T, а просто T).
gehho

5

Не те, що Джон ще не висвітлював це, але ось кілька посилань на блоги та відео від Еріка Ліпперта. Він робить гарну роботу, пояснюючи це на прикладах.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.