Преобразование 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>());
}