Casting List‹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 лет назад. Я не слежу за историей С#, но сейчас она, безусловно, актуальна. :) - 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
ОП спросил, как я могу преобразовать ... в List<IMyClass>, и вы не конвертируете в List<T>, а в IEnumerable<T> - person CodesInChaos; 08.02.2011
comment
@CodeInChaos: Вы правы, это была моя первая версия, и недавно я перешел на IEnumerable<T>. Откат. Спасибо - person abatishchev; 09.02.2011