巴别塔上的雇工


How C# Implements Closure
12月 27, 2008, 9:39 上午
Filed under: 技术体会

从2.0版本开始,C#开始支持Closure的概念,但是可能很多C#程序员还不知道何为Closure,即使曾经用过。了解事情就要了解事情的本质,看看C#到底怎么实现对Closure的支持的。

Wikipedia上,Clousre是这么定义的:

“In computer science, a closure is a function that is evaluated in an environment containing one or more bound variables. When called, the function can access these variables. ”

我觉得这样的定义中用了“function”这个词不大合适,用"code block"也许更恰当。特定于C#来说,Closure就是一段code block,这段code block是一个anonymous method或者lambda expression,而且,这段code block还可以访问它所在block的变量。奇妙的地方是,Closure不一定在所在block中运行,但是依然能够访问所在block中的变量。看代码例子最清楚了

class Program
{
    public static Action GetAction(int arg)
    {
        int x = arg;
        Action action =
            delegate()
            { // this is a closure
                Console.WriteLine(x);
            };
 
        return action;
    }
 
    public static void Main(string[] args)
    {
        Action action = GetAction(13);
        action(); // this is where the closure actually get executed
    }
}

上面的Code编译运行正常,输出13。在GetAction方法中有一段anonymous method,这就是一个Closure,你看它访问了GetAction中的“局部变量”x(这个“局部变量”需要打上引号,下面会介绍为什么),而这段Closure实际上不是在GetAction中执行的,GetAction将这个Closure作为一个Action返回了,在Main函数中,这个Closure才得以执行,也就是说在Main函数中,GetAction中的“局部变量”x才得到访问。

我相信大部分C#程序员都写过类似的code,但是不知道是否都想过,为什么一个“局部变量”会变得不“局部”了,按照常理,x既然是GetAction中的局部变量,是不可能在GetAction函数之外被访问的。这是因为C#支持Closure这个概念,为了支持这个概念,C#编译器会自动产生一些你看不到的code,要看到这些code,用Red Gate’s Reflector是不行的,因为这个Reflector足够聪明,把自动产生的code变回和我们写的code一模一样,要了解C#编译器搞了什么鬼,需要动用ILDASM.exe,虽然看到的是IL代码,但是我们还是能过推出C#编译器做的工作。C#把上面的code实际上变成了类似下面的样子再编译。

class Program
{
    private sealed class <c>__DisplayClass1
    {
        public int x;
 
        public void <GetAction>b__0()
        {
            Console.WriteLine(x);
        }
    }
 
    public static Action GetAction(int arg)
    {
        var captured = new <c>__DisplayClass1();
        captured.x = arg;
        Action action = captured.<GetAction>b__0;
        return action;
    }
 
    public static void Main(string[] args)
    {
        Action action = GetAction(13);
        action(); 
    }
}

可以看到,C#编译器产生了一个新的nested class叫<c>__DisplayClass1,这个nested class中有一个就是Closure内容的方法叫<GetAction>b__0,光看这两个古玲精怪的标识符就知道这肯定是C#编译自自动产生的名字了(实际上上面的code是没法编译的,因为标识符中不能包含"<"和">"字符,这么写是因为C#编译器产生的就是这两个名字),<c>__DisplayClass1还有一个public的data member叫x。

现在,C#如何实现Closure就一目了然了,每一个被Closure使用的“局部变量”,因为Closure“抓住”了,所以叫Captured variable,C#必须保证captured variable的life time必须不能比closure短,所以他们实际上不再是局部变量,而是nested class的成员变量,Closure也不能简单地处理为一个static method,而是变成了nested class的一个非静态成员方法,这样它总能访问同属一个class的data member,这样Captured Variable的life time问题就被解决了。


7条评论 so far
留下评论

Console就是Closure?

评论 由 Jinghua

No. Big No.Read before commenting.

评论 由 Morgan

ok, Console is one kind of Closure.

评论 由 Jinghua

Still, NO. Console has nothing to do with the concept Closure.Hope you have some some time to read this post. It is clearly stated "Closure is … code block".

评论 由 Morgan

仔细看了一下,才明白,我建议不要只写一句Console,多写几句我立刻就能反映到原来Closure是一段代码,这样的注释太容易引起误解了。这就是reader和author的不同之处了。。。。。

评论 由 Jinghua

Glad that you got it.

评论 由 Morgan

呵呵,我也写过一篇关于closure在c#当中的应用。http://littlepeak.yo2.cn/articles/closure-in-c.html

评论 由 gao




留下评论