【问题标题】:How captured value in anonymous methods are implemented in .NET如何在 .NET 中实现匿名方法中捕获的值
【发布时间】:2009-04-12 20:28:56
【问题描述】:

我对实际的 .NET 实现及其背后的决定感到好奇。

例如在 Java 中,匿名类中使用的所有捕获值都必须是最终值。 .NET 中似乎取消了此要求。

另外,值类型与引用类型的捕获值的实现是否存在差异?

谢谢

【问题讨论】:

    标签: c# java .net closures


    【解决方案1】:

    了解它是如何实现的最简单的方法是尝试一下。编写一些使用捕获变量的代码,编译它,然后在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();
            }        
        }
    }
    

    (当然,如果你不复制,你会得到不同的结果 - 实验!)这被编译成这样的代码:

    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);
            }
        }
    }
    

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

    【讨论】:

    • 嗨,非常好的解释。您还可以显示编译器在不复制到局部变量的情况下会产生什么。
    猜你喜欢
    • 1970-01-01
    • 2012-11-01
    • 2017-02-18
    • 1970-01-01
    • 1970-01-01
    • 2010-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多