闭包在计算机学上的定义如下:
“In computer science, a closure is a first-class function with free variables that are bound in the lexical environment.”
懂了没有?没懂,那么继续读下去。
函数是一等公民(Your Functions Are First Class)
那么什么是”first-class function“?它是这样一种函数:我们的编程语言将它与一般数据类型同等对待。这意味着我么可以将一个函数赋值给一个变量、将函数作为参数传递或者像普通函数一样调用它…C#中可以通过匿名方法创建first-class function:
Func<string,string> myFunc = delegate(string var1) { return "some value"; };
或者可以用Lambda表达式创建:
Func<string,string> myFunc = var1 => "some value";
这两种方法在功能上是相同的,它们都创建了一个方法接受一个字符串参数并返回一个字符串。我们可以像调用一般函数那样调用它:
string myVar = myFunc("hello");
自由变量
我们现在拥有了带自由变量的first-class function…什么是自由变量呢?一个自由变量是这样的:它不是函数自身的局部参数或者局部变量,而是引用了函数外部变量的变量。比如下面:
var myVar = "this is good"; Func<string,string> myFunc = delegate(string var1) { return var1 + myVar; };
OK,上面的匿名委托在它的封闭作用域内引用了myVar,而myVar既不是它自己的局部参数,也不是它自己的局部变量。它是一个自由变量,那又怎样呢?
It Has To Close Over It
下面的代码输出什么呢?
static void Main(string[] args) { var inc = GetAFunc(); Console.WriteLine(inc(5)); Console.WriteLine(inc(6)); } public static Func<int,int> GetAFunc() { var myVar = 1; Func<int, int> inc = delegate(int var1) { myVar = myVar + 1; return var1 + myVar; }; return inc; }
当我们调用一个叫”GetAFunc”的方法时,我们获得了这样一个方法:这个方法访问了myVar,而myVar被绑定在匿名委托内部。
但是局部变量不是在栈上创建的吗?当执行完方法后它们不是应该消失吗?一般情况是的。但是这种情况不是,运行代码,我们将得到下面的结果:
而不是某些人想的6、7。因此当我们传回那个方法(即通过var inc = GetAFunc())后,变量myVar将与方法共存而不是立即被销毁。变量myVar超出它的原作用域而存活。正如我们所看到的的,我们调用GetAFuc()两次,它就自增两次。很疯狂是不是?
这就是闭包!我们已经将一些自由变量绑定到了词法环境。
我们可以发现,它很简单。它实际上只是关于如何保持对变量的引用即使已经超出它的作用域。我们可以这么说:这些匿名委托圈住了(closed)这些变量,导致这些变量即使超出词法作用域后却仍然存活。
它是如何做到的?
下一个问题是:它是如何做到的?一般别人问我这个问题时,我会回答地非常”谨慎保守“,但那只在我被惹毛了的情况下。事实上闭包是以一种比你想象中的更直接得多的方式实现的。我们这里做的其实只是将一些数据与方法绑定,然后将方法导出传播。Geez,我非常希望C#有这么个东西…但是等等,我们确实有这么个东西,它既是Class(类)。
当C#编译器检测到一个匿名委托形成闭包(它被传递到当前作用域外部)时,C#编译器会将委托方法和相关的自由变量一起放到一个编译器生成的类里。因此,每次我们调用那个委托方法,实际上调用的是这个编译器生成类的方法。一旦我们不再引用这个委托时,它就会被回收。我们其实可以把上面的代码想象成下面这样(原文没有,我自己加的):
static void Main(string[] args) { var myClass = new CompilerGeneratedClass(); myClass.myVar = 1; Console.WriteLine(myClass.DelegateMethod(5)); Console.WriteLine(myClass.DelegateMethod(6)); } class CompilerGeneratedClass { public int myVar; public void DelegateMethod(int var1) { myVar = myVar + 1; return var1 + myVar; } }
参考自:http://www.codethinked.com/c-closures-explained
其他参考:http://www.cnblogs.com/lxconan/archive/2012/11/23/fun_with_closure.html