这是因为foreach
在您的具体情况下不会进行编译时检查。如果你构建了工作代码,你会得到a InvalidCastException在运行时 http://ideone.com/GmyzWt.
using System.Collections.Generic;
public class Test
{
internal class Program
{
public static void Main()
{
var item = new Foo();
var inner = new List<Foo>();
var outer = new List<List<Foo>>();
inner.Add(item);
outer.Add(inner);
IEnumerable<IEnumerable<Foo>> data = outer;
foreach (Foo foo in data)
{
foo.Bar();
}
}
}
public class Foo
{
public void Bar()
{
}
}
}
doing foreach (Foo foo in data)
相当于调用
IEnumerator enumerator = ((IEnumerable)data).GetEnumerator();
Foo foo; //declared here in C# 4 and older
while(enumerator.MoveNext())
{
//Foo foo; //declared here in C# 5 and newer
foo = (Foo)enumerator.Current; //Here is the run time error in your code.
//The code inside the foreach loop.
{
foo.Bar();
}
}
所以你看它并不关心你传入的是什么类型,只要foo = (Foo)enumerator.Current;
调用成功。
它不抛出任何编译时错误的原因是IEnumerable<T>
is 协变 http://msdn.microsoft.com/en-us/library/dd799517%28v=vs.110%29.aspx。这意味着我可以通过任何基于Foo
或更多源自Foo
。所以如果我有可能创建一个继承自的第二类Foo
这也将支持IEnumerable<Foo>
并让我的列表包含它,否则会导致转换失败。
//This code compiles fine in .NET 4.5 and runs without throwing any errors.
internal class Program
{
public static void Main()
{
var item = new Baz();
var inner = new List<Baz>();
inner.Add(item);
IEnumerable<IEnumerable<Foo>> data = inner;
foreach (Foo foo in data)
{
foo.Bar();
}
}
}
public class Foo
{
public void Bar()
{
}
}
public class Baz : Foo, IEnumerable<Foo>
{
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator<Foo> IEnumerable<Foo>.GetEnumerator()
{
throw new NotImplementedException();
}
}
但是,如果您标记Foo
as sealed
编译器现在知道不再存在派生类,然后将抛出编译器错误