闭包即闭包是指可以包含自由(未绑定到特定对象)变量的代码块.表现出来是调用函数结束后,函数内的变量的生存周期拉长到调用者的生命。很多闭包实现成匿名函数(js也是表现成匿名函数的,其他的方法不清楚),3.0中引入了匿名函数,相应的也提供了闭包的支持。
闭包实例:
class Program { static void Main(string[] args) { Action ss = bibao(); ss(); Console.ReadKey(); } public static Action bibao() { int i = 100; return delegate { Console.WriteLine(i); }; } }
通常情况下{}内的变量在结束后会结束生命周期,然而在这里ss()却可以打印出i的值。生命周期从bibao函数里面拉长到Main里面。
在js里面是通过函数对象之间作用域链的引用关系实现,那么在c#中又是用什么方法实现的呢?
反编译代码:
编译后的代码生成了一个新的类,c#的闭包就是建立在这个类的基础上面的。
其中闭包中的变量作为类的公开成员变量,闭包函数自身作为成员,类型是internal。因为此类和闭包函数所在的类生成在一个同一个程序集中,而闭包流程中并不会使用这个类与其他程序集直接交流。
具体的调用过程
Main中:
.method private hidebysig static void Main(string[] args) cil managed{ .entrypoint // Code size 17 (0x11) .maxstack 8 IL_0000: call class [mscorlib]System.Action ExtendMwthod.Program::bibao() IL_0005: callvirt instance void [mscorlib]System.Action::Invoke() IL_000a: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() IL_000f: pop IL_0010: ret} // end of method Program::Main
直接调用的依然是bibao方法,再invoke,没发现原因。
.method public hidebysig static class [mscorlib]System.Action bibao() cil managed{ // Code size 25 (0x19) .maxstack 8 IL_0000: newobj instance void ExtendMwthod.Program/'<>c__DisplayClass1_0'::.ctor() IL_0005: dup IL_0006: ldc.i4.s 100 IL_0008: stfld int32 ExtendMwthod.Program/'<>c__DisplayClass1_0'::i IL_000d: ldftn instance void ExtendMwthod.Program/'<>c__DisplayClass1_0'::'b__0'() IL_0013: newobj instance void [mscorlib]System.Action::.ctor(object, native int) IL_0018: ret} // end of method Program::bibao
可以看到自动生成的类在这里实例化了,闭包函数内部已经改写了。起始用字段i和方法'<bibao>b__0'实例化了action,因而在main中调用的时候变量已经包含在action的参数里面带过去了。通过这种方法实现了变量生命周期的延长。