C#与闭包
你可能会好奇.net本身并不支持函数对象,那么这样的特性又是从何而来呢?答案是编译器,我们一看IL代码便会明白了。
    首先我给出c#代码:
   
public class TCloser {        public Func<int> T1(){            var n = 10;            return () =>            {                return n;            };        }        public Func<int> T4(){            return () =>            {                var n = 10;                return n;            };        }    } | 
    这两个返回的匿名函数的唯一区别就是返回的委托中变量n的作用域不一样而已,T1中变量n是属于T1的,而在T4中,n则是属于匿名函数本身的。但我们看看IL代码就会发现这里面的大不同了:
   
.method public hidebysig instance class [mscorlib]System.Func`1<int32> T1() cil managed{    .maxstack 3    .locals init (        [0] class ConsoleApplication1.TCloser/<>c__DisplayClass1 CS$<>8__locals2,        [1] class [mscorlib]System.Func`1<int32> CS$1$0000)    L_0000: newobj instance void ConsoleApplication1.TCloser/<>c__DisplayClass1::.ctor()    L_0005: stloc.0    L_0006: nop    L_0007: ldloc.0    L_0008: ldc.i4.s 10    L_000a: stfld int32 ConsoleApplication1.TCloser/<>c__DisplayClass1::n    L_000f: ldloc.0    L_0010: ldftn instance int32 ConsoleApplication1.TCloser/<>c__DisplayClass1::<T1>b__0()    L_0016: newobj instance void [mscorlib]System.Func`1<int32>::.ctor(object, native int)    L_001b: stloc.1    L_001c: br.s L_001e    L_001e: ldloc.1    L_001f: ret}.method public hidebysig instance class [mscorlib]System.Func`1<int32> T4() cil managed{    .maxstack 3    .locals init (        [0] class [mscorlib]System.Func`1<int32> CS$1$0000)    L_0000: nop    L_0001: ldsfld class [mscorlib]System.Func`1<int32> ConsoleApplication1.TCloser::CS$<>9__CachedAnonymousMethodDelegate4    L_0006: brtrue.s L_001b    L_0008: ldnull    L_0009: ldftn int32 ConsoleApplication1.TCloser::<T4>b__3()    L_000f: newobj instance void [mscorlib]System.Func`1<int32>::.ctor(object, native int)    L_0014: stsfld class [mscorlib]System.Func`1<int32> ConsoleApplication1.TCloser::CS$<>9__CachedAnonymousMethodDelegate4    L_0019: br.s L_001b    L_001b: ldsfld class [mscorlib]System.Func`1<int32> ConsoleApplication1.TCloser::CS$<>9__CachedAnonymousMethodDelegate4    L_0020: stloc.0    L_0021: br.s L_0023    L_0023: ldloc.0    L_0024: ret} | 
看IL代码你就会很容易发现其中究竟了,在T1中,函数对返回的匿名委托构造的是一个类,名称为newobj instance void ConsoleApplication1.TCloser/<>c__DisplayClass1::.ctor(),而在T4中,则是仍然是一个普通的Func委托,只不过级别变为类级别了而已。
那我们接着看看T1中声明的类c__DisplayClass1是何方神圣:
.class auto ansi sealed nested private beforefieldinit <>c__DisplayClass1    extends [mscorlib]System.Object{    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed{}    .method public hidebysig instance int32 <T1>b__0() cil managed{}    .field public int32 n} | 
看到这里想必你已经明白了,在C#中,原来闭包只是编译器玩的花招而已,它仍然没有脱离.NET对象生命周期的规则,它将需要修改作用域的变量直接封装到返回的类中变成类的一个属性n,从而保证了变量的生命周期不会随函数T1调用结束而结束,因为变量n在这里已经成了返回的类的一个属性了。
看到这里我想大家应该大体上了解C#闭包的来龙去脉了吧,C#中,闭包其实和类中其他属性、方法是一样的,它们的原则都是下一层可以畅快的调用上一层定义的各种设定,但上一层则不具备访问下一层设定的能力。即类中方法里的变量可以自由访问类中的所有属性和方法,而闭包又可以访问它的上一层即方法中的各种设定。但类不可以访问方法的局部变量,同理,方法也不可以访问其内部定义的匿名函数所定义的局部变量。
                    
                
                
            
        
浙公网安备 33010602011771号