Взгляд на следующий пример (частично взятый из Блога 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)?
Ну, это, безусловно, не будет поддерживаться в 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, вам нужно поддерживать Список, или вы будете счастливы создать новый список? Если это приемлемо, список
- ваш друг.
Он будет работать на C#4 для IEnumerable
, так что вы можете сделать:
IEnumerable<Animal> animals = new List<Giraffe>();
Однако List
не является ковариентной проекцией, так что вы не можете назначать списки, как это было сделано выше, так как вы можете сделать это:
List<Animal> animals = new List<Giraffe>();
animals.Add(new Monkey());
Который явно недействителен.
В терминах 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
и других подобных общих интерфейсов (или даже общих классов), но, ну, по крайней мере, у нас есть кое-что.
, возможно, лучше использовать «ловушку». Ловушка 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());
, но имейте в виду, что вы на самом деле создаете два новых коллекций здесь.