我知道这段代码行不通(以行之有效的方式编写代码也没有问题)。 我想知道编译器如何在没有任何错误的情况下构建。如果你在哪里运行它,你会得到运行时错误? (假设数据不为空)
using System;
using System.Collections.Generic;
public class Class1
{
public void Main()
{
IEnumerable<IEnumerable<Foo>> data = null;
foreach(Foo foo in data){
foo.Bar();
}
}
}
public class Foo {
public void Bar() { }
}
最佳答案
这是因为 foreach
不会在您的特定情况下进行编译时检查。如果您构建了工作代码,您将获得 a InvalidCastException
at run-time .
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()
{
}
}
}
正在做 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>
是covariant .这意味着我可以通过任何基于 Foo
的类(class)或更多来自 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
作为sealed
编译器现在知道不再存在派生类,然后将抛出编译器错误
关于c# - 为什么 C# 编译器对 double IEnumerable<T> 和 foreach T 很满意?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20204925/