2009-10-15 3 views
5

Учитывая следующее: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>>(); 
    } 
    } 
} 

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

+4

Если эта тема интересует вас, вы можете прочитать мою массивную серию на дизайн этой функции в C# 4.0: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+ Contravariance/default.aspx –

ответ

6

Конверсия, которую вы пытаетесь сделать, не поддерживается на версии 3.5 .NET Framework. Он будет поддерживаться в версии 4.0 (Visual Studio 2010) из-за добавления generic covariance/contravariance. В то время как это все равно не позволит вам наложить List<Der> на List<Base>, он будет позволит вам сделать IEnumerator<Der> (что список реализует) до IEnumerator<Base>.

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

+1

Обратите внимание, что общая дисперсия не позволит перечислить список в список , потому что в списке есть как входящие, так и входящие элементы типа T. (It * будет * работать для использования IEnumerable на месте IEnumerable .) – itowlson

+0

Правда, но, если я правильно понимаю, это позволит скомпилировать образец кода OP.'List ' реализует 'IEnumerable ', который может быть переведен в 'IEnumerable '. – bcat

+0

Конечно, я так считаю; Я только упомянул об этом, потому что в тексте вопроса он упоминал о неявных преобразованиях между типами списков, поэтому я хотел уточнить, что ваш комментарий применяется к его коду (который использует IEnumerable), а не к его дополнительному комментарию (который упоминается в списке). – itowlson

1

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

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

Вы всегда могли бы сделать что-то подобное тоже, что бы вернуть IEnumerable класса Base от реализации 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

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

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

1

Вы можете использовать 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>>(); 
     } 
    } 
} 

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

+0

Спасибо, это помогло. Сколько будет замедленное приложение? Значительно ли это влияет на производительность? – SOReader

+0

Выйдите из секундомера, попробуйте его несколько миллионов раз, и тогда вы узнаете. Вы единственный, у кого есть доступ к данным синхронизации для вашего приложения; мы не можем ответить на этот вопрос. –

+0

Успокойся, Эрик =] Просто спросил, проверял ли он когда-либо накладные расходы ... – SOReader

0

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

Я написал метод расширения для IEnumerable, который автоматически бросает TSource всякий раз, когда я хочу, чтобы преобразовать список <Foo> к IEnumerable < Бар > (я все еще на 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>()); 
}