Проблема различия C#: Присвоение Списка <Полученного> как Список <Основа>

Взгляд на следующий пример (частично взятый из Блога MSDN):

class Animal { }
class Giraffe : Animal { }

static void Main(string[] args)
{
    // Array assignment works, but...
    Animal[] animals = new Giraffe[10]; 

    // implicit...
    List animalsList = new List();

    // ...and explicit casting fails
    List animalsList2 = (List) new List();
}

Действительно ли это - проблема ковариантности? Это будет поддерживаться в будущем C#, выпускают и там какие-либо умные обходные решения (использующий только.NET 2.0)?

54
задан AndiDog 9 January 2010 в 05:59
поделиться

4 ответа

Ну, это, безусловно, не будет поддерживаться в C # 4. Фундаментальная проблема:

List<Giraffe> giraffes = new List<Giraffe>();
giraffes.Add(new Giraffe());
List<Animal> animals = giraffes;
animals.Add(new Lion()); // Aargh!

Держите жирафы в безопасности: просто скажите нет небезопасной дисперсии.

Версия массива работает, потому что массивы DO поддерживают дисперсию типа опорных данных, с проверкой времени выполнения. Точка генерала состоит в том, чтобы обеспечить время компиляции безопасность типа.

В C # 4 будет поддержка безопасного общего дисперсии, но только для интерфейсов и делегатов. Поэтому вы сможете сделать:

Func<string> stringFactory = () => "always return this string";
Func<object> objectFactory = stringFactory; // Safe, allowed in C# 4

FUNC Covariant в T , поскольку T используется только в выходе позиция. Сравните это с действие <в T> , которое противоречит в t , потому что t используется только в входном положении, что делает этот сейф:

Action<object> objectAction = x => Console.WriteLine(x.GetHashCode());
Action<string> stringAction = objectAction; // Safe, allowed in C# 4

IENumerable также является ковариантным, что делает это правильным в C # 4, как указано другим:

IEnumerable<Animal> animals = new List<Giraffe>();
// Can't add a Lion to animals, as `IEnumerable<out T>` is a read-only interface.

с точки зрения работы вокруг этого в вашей ситуации в C # 2, вам нужно поддерживать Список, или вы будете счастливы создать новый список? Если это приемлемо, список .Convertall - ваш друг.

112
ответ дан 7 November 2019 в 07:45
поделиться

Он будет работать на C#4 для IEnumerable, так что вы можете сделать:

IEnumerable<Animal> animals = new List<Giraffe>();

Однако List не является ковариентной проекцией, так что вы не можете назначать списки, как это было сделано выше, так как вы можете сделать это:

List<Animal> animals = new List<Giraffe>();
animals.Add(new Monkey());

Который явно недействителен.

16
ответ дан 7 November 2019 в 07:45
поделиться

В терминах List, боюсь, вам не повезло. Однако в .NET 4.0/C# 4.0 добавлена поддержка ковариантных/контравариантных интерфейсов. В частности, IEnumerable теперь определяется как IEnumerable, что означает, что параметр типа теперь является ковариантом.

Это означает, что вы можете сделать что-то подобное в C# 4.0....

// implicit casting
IEnumerable<Animal> animalsList = new List<Giraffe>();

// explicit casting
IEnumerable<Animal> animalsList2 = (IEnumerable<Animal>) new List<Giraffe>();

Примечание: типы массивов также являются ковариантными (по крайней мере, начиная с .NET 1.1).

Жаль, что поддержка дисперсий не была добавлена для IList и других подобных общих интерфейсов (или даже общих классов), но, ну, по крайней мере, у нас есть кое-что.

9
ответ дан 7 November 2019 в 07:45
поделиться

, возможно, лучше использовать «ловушку». Ловушка PowerShell указывает код блокировки для выполнения, когда происходит завершение или ошибка. Тип

Get-Help about_trap

, чтобы узнать больше о выписке ловушки.

-121--526161-

COVARICE / CONTRAVARARICE не может быть подкреплен на мусорных коллекциях, поскольку другие упомянули, потому что невозможно гарантировать безопасность типа как способах компиляции; Тем не менее, можно сделать быстрый односторонний преобразование в C # 3.5, если это то, что вы ищете:

List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = giraffes.Cast<Animal>().ToList();

, конечно, это не то же самое, это на самом деле ковариантно - вы на самом деле создаете другой список , но это «обходной путь» так сказать.

В .NET 2.0 вы можете воспользоваться преимуществами ковариации массива, чтобы упростить код:

List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = new List<Animal>(giraffes.ToArray());

, но имейте в виду, что вы на самом деле создаете два новых коллекций здесь.

5
ответ дан 7 November 2019 в 07:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: