2010-03-22 6 views
5

Скажем, к примеру, у меня есть класс:Почему функция, которая принимает IEnumerable <interface>, не принимает IEnumerable <class>?

public class MyFoo : IMyBar 
{ 
    ... 
} 

Тогда, я хотел бы использовать следующий код:

List<MyFoo> classList = new List<MyFoo>(); 
classList.Add(new MyFoo(1)); 
classList.Add(new MyFoo(2)); 
classList.Add(new MyFoo(3)); 

List<IMyBar> interfaceList = new List<IMyBar>(classList); 

Но это дает ошибку:

`Argument '1': cannot convert from 'IEnumerable<MyFoo>' to 'IEnumerable<IMyBar>' 

Почему это? Поскольку MyFoo реализует IMyBar, можно было бы ожидать, что IEnumerable MyFoo можно рассматривать как IEnumerable из IMyBar. Приземный пример в реальном мире, производящий список автомобилей, а затем сказал, что это не список транспортных средств.

Это лишь незначительное раздражение, но если кто-то может пролить свет на это, я был бы очень обязан.

+1

Ответ вы после этого также был покрыт в этом вопросе: http://stackoverflow.com/questions/2346763/any-simple-way-to- объясните-почему-я-не могу сделать-listanimal-animals-new-arraylistdo/2346857 # 2346857 –

+0

Да, спасибо - я видел это в списке «related», как только я разместил вопрос, но он не появился Я писал это ... –

ответ

14

Это будет работать в C# 4.0! То, о чем вы говорите, называется общая ковариантность.

В то же время вы можете использовать Cast extension method:

List<IMyBar> interfaceList = new List<IMyBar>(classList.Cast<IMyBar>()); 
+1

Хорошо, блестяще, но на самом деле это не похоже на науку о ракетах - есть ли какая-то особая причина, почему она ранее не поддерживалась? –

+0

@Matt: Это не наука о ракетах, но, как любая функция, требуется время и деньги для реализации, и были некоторые отличные функции с более высоким приоритетом, которые они реализовали раньше (например, LINQ действительно хорош, не так ли?). Вам нужно будет вырезать несколько отличных функций для доставки продукта вовремя. - Одно замечание об этой конкретной функции заключается в том, что она требует поддержки от базовой CLR, а CLR 2.0 не поддерживает ее. Поддержка добавлена ​​в CLR 4.0. –

+0

Спасибо - очень полезно ... :) –

3

Это поддерживается в .NET 4.0, но не раньше.

http://msdn.microsoft.com/en-us/library/dd799517%28VS.100%29.aspx

+1

Технически это поддерживалось в .Net через IL с 2.0, но только получило поддержку в C# и VB.Net в 4.0 – JaredPar

+0

@ JaredPar: Неужели? Поддерживает ли CLR 2.0 общую ковариацию в * проверяемом * IL? –

+0

@Mehrdad, Да, я так считаю. Прошло уже 1 год с тех пор, как у меня была эта дискуссия с Lucian, но мое текущее воспоминание заключается в том, что CLR поддерживает co/contravariance с 2.0 и что 4.0 все в компиляторах (возможно, исправление ошибки или два в CLR). – JaredPar

3

Чтобы уточнить, почему он сейчас не работает, потому что IEnumerable<MyClass> не наследует от IEnumerable<MyBar>. Я еще раз скажу: перечислимый производный тип не наследуется от перечислимого базового типа. Скорее, оба типа специализируют тип IEnumerable<T>. В .Net 3.5 и ниже у них нет других отношений за пределами специализации (что не является наследованием) и в противном случае это два совершенно разных типа в системе типов.

.Net 4.0 не изменит способ, которым эти типы связаны вообще. Они по-прежнему будут двух совершенно разных типов, которые связаны только с помощью специализации. То, что он будет делать, это позволить вам писать методы, которые знают об отношениях специализации, и в некоторых случаях позволяют вам подставлять их, когда вы пишете другой.

+1

Чтобы прояснить ваш второй абзац: точка универсальной дисперсии заключается в том, что она изменяет отношение «присваивание совместимо с» на определенных общих типах. Это соответствующее отношение; как вы говорите, соотношение наследования остается неизменным. –

+0

Джоэл, я так и не думал о сопутствующей дисперсии/противоречивости. Очень полезный ответ, +1 от меня. – SolutionYogi

1

Если вы хотите бросить между IEnumerables (как вы писали в заголовке своего вопроса), а не между списками (как вы писали в своем вопросе), вы можете дождаться covariance C# 4.0. До этого дня вы можете использовать методы расширения. Но я бы не использовал Cast extension method, отмеченный в других ответах, но написал свой собственный метод, который можно использовать только для ковариационного литья в IEnumerable. Когда/Если вы переключитесь на C# 4.0, вы легко найдете все места в своем коде, где литье будет избыточным.

public static class cEnumerableExtensions 
{ 
    public static IEnumerable<TResult> CovarianceConversion<TResult, TSource>(this IEnumerable<TSource> source) 
     where TSource : TResult 
    { 
     foreach (var item in source) 
     { 
      yield return item; 
     } 
    } 
} 

Окончательный узел. Там, кажется, no precompiler constant for the netframework 4.0, в противном случае я бы добавить

#if DOTNET4 
    [Obsolete("You can directly cast in C# 4.0.")] 
    #endif 
Смежные вопросы