为什么我不能将List <List <Foo >>传递给IEnumerable <IEnumerable <Foo >>

Dan*_*ely 11 c# generics .net-3.5

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

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

    UseFoos(myFoos);
}

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

}
Run Code Online (Sandbox Code Playgroud)

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它失败的类型的内部组件有什么不同?

Jon*_*eet 19

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

UseFoos(myFoos.Cast<IEnumerable<Foo>>());
Run Code Online (Sandbox Code Playgroud)

该代码UseFoos在C#4下编译得很好(当你给出参数名称时),它引入了接口和委托的通用协方差和逆变.

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

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;
Run Code Online (Sandbox Code Playgroud)

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

// This won't work
List<string> strings = new List<string>();
List<object> objects = strings;
Run Code Online (Sandbox Code Playgroud)

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

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

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

public interface IEnumerable<out T>
Run Code Online (Sandbox Code Playgroud)

在哪里out宣布协方差T.

埃里克利珀有很多在他的博客类别详细了解此协方差和逆变.