Кастинг списък‹T› - проблем с ковариация/контравариация

Предвид следните видове:

public interface IMyClass { }
public class MyClass : IMyClass { }

Чудя се как мога да конвертирам List<MyClass> в List<IMyClass>? Не съм напълно наясно с темите за ковариация/контравариантност, но разбирам, че не мога просто да хвърля списъка просто поради това.

Мога да измисля само това тривиално решение; липса на всякаква елегантност, загуба на ресурси:

...
public List<IMyClass> ConvertItems(List<MyClass> input)
{
   var result = new List<IMyClass>(input.Count);
   foreach (var item in input)
   {
       result.Add(item);
   }
   return result;
}
....

Как можете да го разрешите по по-елегантен/ефективен начин?

(Моля, имайте предвид, че имам нужда от решение .NET 2.0, но за пълнота ще се радвам да видя и по-елегантните решения, използващи по-нови версии на рамка.)


person user256890    schedule 08.02.2011    source източник


Отговори (3)


Най-простият начин вероятно е да използвате ConvertAll:

List<IMyClass> converted = original.ConvertAll<IMyClass>(x => x);

Дори ако използвате .NET 2, можете да използвате ламбда синтаксис, ако използвате VS2008 или по-нова версия. В противен случай винаги има анонимни методи:

List<IMyClass> converted = original.ConvertAll<IMyClass>(
    delegate (MyClass x) { return x; });

В .NET 3.5 можете да използвате LINQ с Cast, OfType или дори само Select:

var converted = original.Cast<IMyClass>().ToList();
var converted = original.OfType<IMyClass>().ToList();
var converted = original.Select(x => (IMyClass) x).ToList();

В .NET 4.0 можете да използвате ToList директно без междинно преобразуване, поради ковариацията на IEnumerable<T>:

var converted = original.ToList<IMyClass>();
person Jon Skeet    schedule 08.02.2011
comment
Това е подла употреба на ковариация; Много добре. - person Ani; 08.02.2011

Трябва ли да е списък? IEnumerable решение може да бъде по-ефективно:

 public IEnumerable<IMyClass> ConvertItems(List<MyClass> input)
 {
    foreach (var item in input)
    {
        yield return (IMyClass)item;
    }
 } 
person cjk    schedule 08.02.2011
comment
Струва си да се каже, че не е задължително да правите това, за да използвате IEnumerable решение. Методът може просто да направи return input;. Това би означавало, че основният обект все още е списъкът и можете да се върнете към него - проблем, който не откривате с горния метод. Но ковариацията означава, че директното прехвърляне към IEnumerable<IMyClass> е много възможно. Не съм сигурен дали това е нова промяна, тъй като този отговор беше направен за първи път преди 7 години. Не следя историята на c#, но със сигурност е валидна сега. :) - person Chris; 08.03.2018

(.NET 3.5 решение)

List<MyClass> list = new List<MyClass> { ... };
List<IMyClass> converted = list.Cast<IMyClass>().ToList();
person abatishchev    schedule 08.02.2011
comment
преобразуваното е IEnumerable<IMyClass>, а не List<IMyClass> - person CodesInChaos; 08.02.2011
comment
OP попита как мога да конвертирам ... в List<IMyClass>, а вие не конвертирате в List<T>, а в IEnumerable<T> - person CodesInChaos; 08.02.2011
comment
@CodeInChaos: Прав си, това беше първата ми версия и напоследък преминах към IEnumerable<T>. Навита назад. Благодаря - person abatishchev; 09.02.2011