什么是Closure?
在我的这篇博客中谈到了代理的行为不是我期望的结果的疑问。经过学习理解发现是有关Closure的知识,Closure是在函数式编程比如F#,Haskell等中的一个重要的概念。Clousre是指外部变量被绑定在函数内部。这样理解起来可能比较抽象,用C#示例代码说起来可能比较容易理解,在C#中涉及到Closure的主要是匿名代理和lambda表达式等。首先看一个示例:using System;
namespace Test
{
class Program
{
static Action GetPrinter()
{
int i = 0;
Action action = () => Console.WriteLine(i);
return action;
}
static void Main(string[] args)
{
Action action = GetPrinter();
action();
}
}
}
namespace Test
{
class Program
{
static Action GetPrinter()
{
int i = 0;
Action action = () => Console.WriteLine(i);
return action;
}
static void Main(string[] args)
{
Action action = GetPrinter();
action();
}
}
}
//Loops all the objects and creates operations.
foreach (var myObject in myObjects)
{
//Instantiates an operation that printing my object's name.
Action action = () => { Console.WriteLine(myObject.Name); };
operators.Add(new Operation(action));
}
foreach (var myObject in myObjects)
{
//Instantiates an operation that printing my object's name.
Action action = () => { Console.WriteLine(myObject.Name); };
operators.Add(new Operation(action));
}
对于每一次循环而言,可以正确的生成一个Action,这个Action引用到了当前的循环变量myObject,然后当循环变量被替换到下个myObject时,已生成的Action也都指向了当前的myObject。以此类推,到最后所有的Action引用的都是最后一个myObject,也就造成了文中所述的结果。或许你认为这是Closure的一个缺点,或者是一个特有的功能。不管如何,Closure的行为就是这样,了解了它的行为我们便可在今后的开发中避免一些不必要的错误。
那么有个疑问是,编译器是如何构造Closure的呢?
对于Closure,C#编译器会为其构造出一个类来,类中的某个函数即为用户定义的代理(比如本文中的Action action),而此代理所引用的的变量(专指来自于所定义代理的环境中的变量,比如本文中的 int i)则变成编译器所构造出来的类的某一个成员。也就是通过这样的行为,编译器成功的实现了Closure。对于本文中的例子,编译器为其自动生成的的类可以这样描述:
class ActionClosure
{
public int i = 0;
public void action()
{
Console.WriteLine(i);
}
}
{
public int i = 0;
public void action()
{
Console.WriteLine(i);
}
}
例如下面的代码:
int i = 0;
Action action = () => Console.WriteLine(i);
i = 1;
Action action = () => Console.WriteLine(i);
i = 1;
会导致ActionClosure发生如下代码所示的行为:
ActionClosure closure = …
closure.i = 0;
closure.i = 1;
本文参考了这篇很不错的文章:http://diditwith.net/2007/02/09/WhatsInAClosure.aspx,本文所引用的内容版权归原作者所有。