为什么我不能传递List <list <foo>&gt;到IEnumerable <ienumerable <foo>&gt; </ienumerable <foo> </list <foo>

时间:2011-12-21 15:49:48

标签: c# generics .net-3.5

此代码生成两个编译时错误:

private void DoSomething()
{
    List<List<Foo>> myFoos = GetFoos();

    UseFoos(myFoos);
}

private void UseFoos(IEnumerable<IEnumerable<Foo>>)
{

}

The best overloaded method match for 'NameSpace.Class.UseFoos(System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable<Foo>>)' has some invalid arguments

Argument 1: cannot convert from 'System.Collections.Generic.List<System.Collections.Generic.List<Foo>>' to 'System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable<Foo>>'

转换为IEnumberable<List<Foo>>不是问题。如何构建它失败的类型的内部List组件有什么不同?

1 个答案:

答案 0 :(得分:19)

编辑:我刚刚意识到我还没有真正回答如何解决限制问题。幸运的是,这很容易:

UseFoos(myFoos.Cast<IEnumerable<Foo>>());

在C#4下,该代码编译得很好(当你给UseFoos参数一个名字时),这为接口和代表引入了通用协方差和逆变

作为一个更简单的例子,这适用于C#4,但不适用于C#3:

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;

请注意,即使在C#4中,课程也不是不变的,所以这不会起作用:

// This won't work
List<string> strings = new List<string>();
List<object> objects = strings;

......甚至对于界面,只有在安全的情况下才支持它:

// This won't work either
IList<string> strings = new List<string>();
IList<object> objects = strings;

接口(或委托)必须声明类型参数本身的方差,所以如果你看一下.NET 4 documentation for IEnumerable<T>,你会看到它被声明为

public interface IEnumerable<out T>

其中outT声明了协方差。

Eric Lippert在他的博客类别covariance and contravariance中有更多关于此事的很多