如何在.NET中实现匿名方法中的捕获值

4 .net c# java closures

我很好奇.NET实现及其背后的决定.

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

此外,与参考类型相比,值类型的捕获值的实现是否存在差异?

谢谢

Jon*_*eet 9

找出它是如何实现的最简单方法是尝试它.编写一些使用捕获变量的代码,编译它,然后在Reflector中查看它.请注意,它是捕获的变量,而不是.这是Java和C#在这个领域的重大差异之一.

基本思想是包含至少一个捕获变量的每个范围级别都会产生一个新类,其中包含已捕获变量的字段.如果有多个级别,那么内部范围也有一个字段用于下一个范围输出,依此类推.堆栈上的真正局部变量最终是对自动生成的类的实例的引用.

这是一个例子:

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();
        }        
    }
}
Run Code Online (Sandbox Code Playgroud)

(如果你没有复制课程,你会得到不同的结果 - 实验!)这被编译成如下代码:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

对捕获变量的每个引用最终都会通过生成的类的实例,因此它不仅仅是一次性副本.(好吧,在这种情况下,代码中没有别的东西使用捕获的变量,但你可以很容易地想象它可以.)注意外循环的任何一次迭代,五个新实例都共享一个实例OuterScope.您可能想尝试在委托中使用额外的代码来查看它是如何影响事情的 - 如果委托更改copyofI,那么更改将在下一个委托中看到; copyOfJ将无法看到更改,因为下一个委托将使用单独的实例InnerScope.