为什么 C# 编译器对 double IEnumerable 和 foreach T 感到满意?

2024-04-22

我知道这段代码不起作用(并且以有效的方式编写它没有问题)。 我想知道编译器如何构建而不出现任何错误。如果您在哪里运行它,您会遇到运行时错误吗? (假设数据不为空)

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在运行时 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编译器现在知道不再存在派生类,然后将抛出编译器错误

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 C# 编译器对 double IEnumerable 和 foreach T 感到满意? 的相关文章

随机推荐