在匿名方法如何捕捉值在.NET实现方法、NET

2023-09-03 01:16:02 作者:背叛゜全杀无赦

我很好奇的实际.NET实现和它背后​​的决定。

I am curious about the actual .NET implementation and the decision behind it.

例如在Java中,一个匿名类中使用的所有捕获的值都必须是最终的。这一要求似乎在.NET中被丢弃。

For example in Java, all captured values used in an anonymous classes are required to be final. This requirement seems to be dropped in .NET.

此外,有没有在捕获的值的值类型的实现的差异,而不是引用类型?

Also, is there a difference in an implementation of captured values for value types as opposed to reference types?

感谢

推荐答案

找出它是如何实现的最简单的方法就是去尝试。写一些code,它使用捕获变量,编译它,然后看它在反射。需要注意的是它的的变量的被抓获,而不是值的。这是在这一领域的Java和C#的一个重要区别。

The easiest way of finding out how it's implemented is to try it. Write some code which uses a captured variable, compile it, then look at it in Reflector. Note that it's the variable which is captured, not the value. That's one of the big differences between Java and C# in this area.

的基本思想是,包含的范围的每个级别中一个新的类中的至少一个捕获的可变的结果与已捕获的变量字段。如果有一个以上的水平,则内部范围也具有为下范围出一个场,等等。在堆栈上的真正的局部变量最终会被引用的自动生成的类的实例。

The basic idea is that each level of scope containing at least one captured variable results in a new class with fields for the variables which have been captured. If there's more than one level, then an inner scope also has a field for the next scope out, and so on. The genuine local variables on the stack end up being references to instances of the autogenerated classes.

下面是一个例子:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<Action> actions = new List<Action>();

        for (int i=0; i < 5; i++)
        {
            int copyOfI = i;

            for (int j=0; j < 5; j++)
            {
                int copyOfJ = j;

                actions.Add(delegate
                {
                    Console.WriteLine("{0} {1}", copyOfI, copyOfJ);
                });
            }
        }

        foreach (Action action in actions)
        {
            action();
        }        
    }
}

(你得到不同的结果,如果你不走,当然副本 - 实验!)这是编译为code是这样的:

(You get different results if you don't take a copy of course - experiment!) This is compiled into code like this:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<Action> actions = new List<Action>();

        for (int i=0; i < 5; i++)
        {
            OuterScope outer = new OuterScope();
            outer.copyOfI = i;

            for (int j=0; j < 5; j++)
            {
                InnerScope inner = new InnerScope();
                inner.outer = outer;
                inner.copyOfJ = j;

                actions.Add(inner.Action);
            }
        }

        foreach (Action action in actions)
        {
            action();
        }        
    }

    class OuterScope
    {
        public int copyOfI;
    }

    class InnerScope
    {
        public int copyOfJ;
        public OuterScope outer;

        public void Action()
        {
            Console.WriteLine("{0} {1}", outer.copyOfI, copyOfJ);
        }
    }
}

每的引用捕获变量最终会通过生​​成的类的实例,所以它不只是一个一次性的副本。 (好吧,在这种情况下,没有其他的code使用捕获的变量,但你可以想像它可以。)请注意如何为外循环中的任何一个迭代中,五个新的实例 OuterScope 。你可能会想尝试用额外的code玩的委托,看看它如何影响的东西 - 如果委托修改 copyofI 的变化将在未来委托中可以看出;修改 copyOfJ 将不可见,因为接下来的委托将使用 InnerScope 的一个单独的实例。

Every reference to the captured variable ends up going through the instance of the generated class, so it's not just a one-off copy. (Okay, in this case nothing else in the code uses the captured variables, but you can easily imagine it could.) Note how for any one iteration of the outer loop, the five new instances all share one instance of OuterScope. You might want to try playing with extra code in the delegate to see how that affects things - if the delegate changes copyofI that change will be seen in the next delegate; changes to copyOfJ won't be seen because the next delegate will be using a separate instance of InnerScope.