匿名方法

与前面的可空类型是一样的,匿名方法也是C# 2.0里面提出来的。

1 匿名方法

   1.1 什么是匿名方法?

        顾名思义,就是没有名称的方法,因为没有名称,匿名方法只能在函数定义(匿名方法是把方法的实现和定义嵌套在了一起)的时候被调用,在其他任何情况下都不能被调用。对于编译器来说,匿名方法并不是没有名字的,编译器在编译匿名方法时会为其生成一个方法名,下面可以通过IL代码来证明。

   

 1 namespace 匿名方法
 2 {
 3     class Program
 4     {
 5         private delegate void VoteDelegate(string name);
 6         static void Main(string[] args)
 7         {
 8             //使用匿名方法来实例化委托对象
 9             VoteDelegate voteDelegate = delegate(string name)
10             {
11                 Console.WriteLine("昵称为:{0} 来帮我提高啦",name);
12             };
13             //通过调用委托来回调Vote方法,这是隐式调用方式
14             voteDelegate("Hong");
15             Console.ReadKey();
16         }
17     }
18 }

    从以上的代码可以看出,委托对象可用匿名方法来实例化的。当并不能一概而论,匿名方法也有它本身的缺点——不能在其他地方被调用,即不具有复用性。而且,匿名方法会自动形成“闭包”。当一个函数(这里成为外部函数)包含对另一个函数(内部函数)的调用时,或当内部函数使用了外部函数的变量时,都会形成闭包。闭包可能会延长外部变量的生命周期。所以如果委托包装的方法相对简单,并且该方法在其他地方的调用频率较低,你就可以考虑用匿名方法来实例化委托对象。

    

    1.2 对变量捕捉过程的剖析

         变量被匿名方法捕获后,变量的生命周期会被延长,就是说对于一个被捕获的变量而言,只要还有任何委托实例在引用它,它就一直存在,就不会在部分委托实例调用结束后被垃圾回收释放掉。下面通过代码来解释:

 1 class Program
 2     {
 3         private delegate void ClosureDelegate();
 4         static void Main(string[] args)
 5         {
 6             ClosureDelegate test = CreateDelegateInstance();
 7             test();
 8             Console.ReadKey();
 9         }
10 
11         private static ClosureDelegate CreateDelegateInstance()
12         {
13             //外部变量
14             int count = 1;
15             ClosureDelegate closureDelegate = delegate()
16             {
17                 Console.WriteLine(count);
18                 count++;
19             };
20             closureDelegate();
21             return closureDelegate;
22         }
23     }

结果:

    我并不知道大家对这个结果是否感到惊讶,第一次输出1是正常的,但第二个输出2确实比较奇妙的。大家可能会认为应该抛出异常才对,因为count在栈上的生命周期已经结束。但结果确实是输出2,是不会错的,我们可以倒推原因,使用匿名方法时,编译器会创建一个额外的类来容纳变量,此时count变量会被分配到堆上的。CreateDelegateInstance方法有该类的一个实例引用,所以此时匿名方法捕捉到的是变量count的一个引用,而不是真真的值。在这个过程中,匿名方法延长了变量count的生命周期。

    对于匿名方法捕捉到的变量,编译器会额外创建一个类来容纳它们。

    

    由上图可知,我们并没有在源码中定义<>c_DisplayClass2_0类。这个类就是编译器创建来容纳捕获的变量count的,该类还容纳了CreateDelegateInstance的方法。

原文地址:https://www.cnblogs.com/Helius/p/5751435.html