Почему функция, которая принимает IEnumerable <interface>, не принимает IEnumerable <class>? - PullRequest
5 голосов
/ 23 марта 2010

Скажем, например, у меня есть класс:

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.Обычный пример из реальной жизни: создание списка автомобилей, а затем сообщение о том, что это не список автомобилей.

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

Ответы [ 4 ]

14 голосов
/ 23 марта 2010

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

Тем временем вы можете использовать Cast метод расширения :

List<IMyBar> interfaceList = new List<IMyBar>(classList.Cast<IMyBar>());
3 голосов
/ 23 марта 2010

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

3 голосов
/ 23 марта 2010

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

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

1 голос
/ 23 марта 2010

Если вы хотите выполнять разбор между IEnumerables (как вы написали в заголовке вашего вопроса), а не между списками (как вы написали в своем вопросе), вы можете подождать ковариационную функцию c # 4.0.До этого дня вы можете использовать методы расширения.Но я бы не стал использовать метод расширения Cast , отмеченный в других ответах, но написал свой собственный метод, который можно использовать только для ковариантного приведения в 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;
        }
    }
}

Последний узел.Кажется, нет константы прекомпилятора для netframework 4.0 , иначе я бы добавил

    #if DOTNET4
    [Obsolete("You can directly cast in C# 4.0.")]
    #endif
...