Преобразование IEnumerable <T>

Учитывая следующее:


class Base<T> {/*...*/}
class Der<T>: Base<T> {/*...*/}

interface Sth<T>{
  IEnumerable<Base<T>> Foo {get;}
}

// and implementation...
class Impl<T>: Sth<T> {
  public IEnumerable<Base<T>> Foo {
    get {
      return new List<Der<T>>();
    }
  }
}

Как это сделать? Ошибка, очевидно, не является неявным преобразованием, найденным в List < Der < T → для List < Base < T → . Если я его явно укажу, произойдет InvalidCastException.

Ответы

Ответ 1

Преобразование, которое вы пытаетесь сделать, не поддерживается версией 3.5.NET Framework. Он будет поддерживаться в версии 4.0 (Visual Studio 2010) из-за добавления общей ковариации/контравариантности. Хотя это все равно не позволит вам отбрасывать List<Der> в List<Base>, это позволит вам использовать IEnumerator<Der> (который реализует список) до IEnumerator<Base>.

В то же время вы можете написать свой собственный класс, который реализует IEnumerable<Base> и возвращает пользовательский IEnumerator<Base>, который просто обертывает List<Der> IEnumerator<Der>. Кроме того, если вы используете .NET Framework версии 3.5, вы можете использовать метод расширения Cast, как это предполагали другие.

Ответ 2

List<Der<T>> не конвертируется в List<Base<T>>, потому что последний может иметь добавленный Base<T>, а первый не может.

Вы можете разрешить это с помощью метода расширения Cast: return new List<Der<T>>().Cast<Base<T>>();

Ответ 3

Чтобы скомпилировать его...

class Impl<T> : Sth<T>
{
    public IEnumerable<Base<T>> Foo
    {
        get
        {
            return new List<Base<T>>(); //change the List type to Base here
        }
    }
} 

Вы всегда можете сделать что-то подобное, что вернет IEnumerable базового класса из реализации Der

class Impl<T> : Sth<T>
{
    public IEnumerable<Base<T>> Foo
    {
        get
        {
            List<Der<T>> x = new List<Der<T>>();

            foreach (Der<T> dt in x)
            {
                yield return dt;
            }
        }
    }
} 

Ответ 4

Вы можете использовать LINQ для перевода из List<Der<T>> в IEnumerable<Base<T>>, с помощью:

class Impl<T>: Sth<T>
{
    public IEnumerable<Base<T>> Foo
    {
        get
        {
            return new List<Der<T>>().Cast<Base<T>>();
        }
    }
}

Как указывали другие ответы, общая конвариантность не поддерживается в версии 3.5, но вы можете использовать LINQ для создания объекта-оболочки, который реализует IEnumerable<Base<T>>.

Ответ 5

Эти ответы помогли мне, и я просто хотел опубликовать свое решение для аналогичной проблемы, которую я имел.

Я написал метод расширения для IEnumerable, который автоматически передает TSource всякий раз, когда я хочу преобразовать List <Foo> к IEnumerable <Bar> (Я все еще на 3.5).

public static SpecStatus Evaluate<TSource, TSpecSource>(this IEnumerable<TSource> source, ISpec<IEnumerable<TSpecSource>> spec)
        where TSource : TSpecSource
{
    return spec.Evaluate(source.Cast<TSpecSource>());
}