关于泛型:为什么一个函数采用IEnumerable< interface>

Why does a function that takes IEnumerable<interface> not accept IEnumerable<class>?

比如说,我有一门课:

1
2
3
4
public class MyFoo : IMyBar
{
    ...
}

然后,我想使用以下代码:

1
2
3
4
5
6
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);

但这会产生错误:

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

为什么会这样?由于MyFoo实现了IMYBAR,因此可以将MyFoo的IEnumerable视为IMYBAR的IEnumerable。一个平凡的现实世界的例子是,生产一份汽车清单,然后被告知它不是一份汽车清单。

这只是一个小小的烦恼,但如果有人能对此有所了解,我将非常感激。


这将在C 4.0中工作!你所说的是一般协方差。

同时可以使用Cast扩展方法:

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


要澄清的是,它现在不起作用的原因是IEnumerable不是从IEnumerable继承的。我再说一遍:派生类型的可枚举不从基类型的可枚举继承。相反,这两种类型都专门化了IEnumerable泛型类型。在.NET 3.5及更低版本中,它们除了专门化(不是继承)之外没有其他关系,否则在类型系统中是两种完全不同的类型。

.NET 4.0根本不会更改这些类型的关联方式。它们仍然是两种完全不同的类型,只通过专门化来联系。它将要做的是允许您编写了解专门化关系的方法,并且在某些情况下允许您在编写另一个方法时替换一个方法。


这在.NET 4.0中受支持,但不早于.NET 4.0。

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


如果您想在IEnumerable(如您在问题标题中所写)和列表(如您在问题中所写)之间进行强制转换,可以等待C 4.0协方差特性。在那之前,你可以使用扩展方法。但我不会使用其他答案中提到的强制转换扩展方法,而是编写自己的方法,它只能用于IEnumerable中的协方差强制转换。当/如果您切换到C 4.0,您将很容易在代码中找到强制转换多余的所有位置。

1
2
3
4
5
6
7
8
9
10
11
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,似乎没有预编译器常量,否则我将添加

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