如何在.NET中实现匿名方法中捕获的值 [英] How captured value in anonymous methods are implemented in .NET

查看:66
本文介绍了如何在.NET中实现匿名方法中捕获的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



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



此外,对于值类型而不是引用类型,捕获的值的实现是否有区别?


$ b

感谢

解决方案

了解如何实现的最简单的方法是尝试。编写一些使用捕获的变量的代码,编译它,然后在 Reflector 。请注意,它是捕获的变量,而不是。这是Java和C#在这一领域的一个很大的区别。



基本思想是每个级别的范围包含至少一个捕获的变量导致一个新类已捕获的变量的字段。如果有多个级别,则内部作用域还具有用于下一个作用域的字段,等等。



下面是一个例子:

 使用System; 
using System.Collections.Generic;

类程序
{
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(委托
{
Console.WriteLine({0} {1},copyOfI,copyOfJ);
});
}
}

foreach(操作中的操作操作)
{
action();
}
}
}

你不用复制课程 - 实验!)这是编译成这样的代码:

 使用System; 
using System.Collections.Generic;

类程序
{
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();
}
}

class OuterScope
{
public int copyOfI;
}

class InnerScope
{
public int copyOfJ;
public OuterScope outer;

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

em>对捕获变量的引用结束了生成类的实例,因此它不只是一次性副本。 (好吧,在这种情况下,代码中没有其他任何东西使用捕获的变量,但你可以容易地想象它可以。)注意如何对外循环的任何一个迭代,五个新实例都共享一个实例 OuterScope 。你可能想尝试在代理中使用额外的代码来看看如何影响事情 - 如果委托更改 copyofI ,将在下一个委托中看到更改;将不会看到对 copyOfJ 的更改,因为下一个委托将使用 InnerScope 的单独实例。


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

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?

Thanks

解决方案

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.

Here's an example:

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

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

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.

这篇关于如何在.NET中实现匿名方法中捕获的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆