c# - 为什么 C# 编译器对 double IEnumerable<T> 和 foreach T 很满意?

标签 c# .net foreach ienumerable

我知道这段代码行不通(以行之有效的方式编写代码也没有问题)。 我想知道编译器如何在没有任何错误的情况下构建。如果你在哪里运行它,你会得到运行时错误? (假设数据不为空)

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/

相关文章:

c# - 反射排除基类的所有属性和所有其他派生类的特定属性

.net - 在 resx 文件中捕获丢失的本地资源

loops - lisp 错误处理 - 检查变量是否为 nil

c# - 单击 setup.exe 时无法获取应用程序启动路径

c# - 获取用户密码配置文件

c# - 是否可以覆盖 ResolveUrl?

javascript - zlib header 检查不正确

php - 根据数据库值勾选复选框

c# - GetFiles 在网络驱动器上太慢

c# - System.Data.OleDb.OleDbException 需要一个或多个参数