C#委托使用匿名方法(一)


  最近看到一本书写到关于匿名方法的使用,写的比较深刻,今天在这里总结一下。由于第一次写博客,如果有不妥的地方,请大家见谅,下面我们转入正题:

  1.委托

  使用委托工作有两种方式:一种是事先定义好一个方法,然后委托到该方法上,另外一种就是直接在代码中使用匿名方法。

  直接使用委托:

        public delegate void Print(string printStr);
        static void Main(string[] args)
        {
            Print print = DelegateMethod;
            print("This is the first DelegateFunction!");
            Console.Read();
        }
        public static void DelegateMethod(string printStr)
        {
            Console.WriteLine(printStr);
        }        

  使用匿名方法:

  static void Main(string[] args)
        {
         Print print = delegate(string printStr)
            {
                Console.WriteLine(printStr + "the first DelegateFunction!");
            };
            print("This is ");
            Console.Read();
         }                                

  在使用匿名方法时候,要注意不能使用跳转语句跳转到该匿名方法的外部,同样不能用跳转语句从外部跳转到匿名方法内部,匿名方法中不能访问不安全代码(unsafe),也 不能访问在匿名方法外部使用的ref和out参数。

  2.匿名方法

  匿名方法在上面已经进行了简单的使用,但是在实际问题中可能遇到的问题要比上面的代码复杂得多,在匿名方法中捕获变量就是难点之一。

  一个简单的例子(我们将上面的代码进行简单的改变):

     public delegate void Print();
        static void Main(string[] args)
        {
            string temp = "this is ";
            string localStr = " the first ";
            Print print = delegate()
            {
                Console.WriteLine(temp + localStr + "DelegateFunction!");
            };
            print();
            Console.Read();
        }

 

  和你预期的相同,控制台会显示“this is the first DelegateFunction!”。然而我们进行一下改变:

  public delegate void Print();
        static void Main(string[] args)
        {
            string temp = "this is ";
            string localStr = " the first ";
            Print print = delegate()
            {
                Console.WriteLine(temp + localStr + "DelegateFunction!");
                localStr = " the second ";
            };
            localStr = " the third ";
            print();//打印出 this is the third DelegateFunction!
            print();//打印出 this is the second DelegateFunction!
            Console.Read();
        }

  我们来分析一下结果:首先temp作为匿名方法外部变量,在匿名方法中没有对它进行操作,localStr 在匿名方法中我们捕获它并赋值为“the second” ,然而从结果分析来 看这个赋值的操作显然是在打印出this is the third DelegateFunction!之后进行的,然后第二次调用匿名方法时候,才将this is the second DelegateFunction结果打印出来。上面的操作告诉我们,只有在调用匿名方法委托实例的时候才会执行匿名方法内部的操作。单纯的创建委托实例并不会立即执行匿名方法代码块。

   3.匿名方法捕获的变量会延长变量的生存周期

    对于被捕获的变量(localStr)只要有委托实例在引用它,它就会一直存在。

     static void Main(string[] args)
        {
            MethodInvoker newDelegate = CreateDelegate();
            newDelegate();//第二次输出This  tempCount is 5
            Console.Read();
        }
      static MethodInvoker CreateDelegate()
        {
            int tempCount;
            MethodInvoker method = delegate
            {
                tempCount = 5;
                Console.WriteLine("This  tempCount is  {0}", tempCount);
                tempCount++;
            };
            method();//第一次输出This  tempCount is 5
            return method;
        }

  上面的代码输出的结果是两行:This  tempCount is  5。在第二次调用的时候可以看到,正常来说tempCount是CreateDelegate()的局部变量,是在栈上的,在CreateDelegate()方法结束后tempCount会随着一起在栈中被销毁,然而在第二次调用匿名方法的时候tempCount的值会依然存在。秘密在于tempCount并不在栈上,事实上编译器创建了一个额外的类来存储变量,CreateDelegate()方法对该类有一个实例引用,所以它可以使用tempCount,而委托也对该实例有一个引用,除非委托准备好被GC回收,否则那个实例是不会被回收的,这样 出现上面的结果也就是正常的。

  4.局部变量的“实例化”

  我们对上面的代码进行简单的更改:

        MethodInvoker newDelegate = CreateDelegate();
            newDelegate();//This  tempCount is 5 ;This  tempOtherCount is 6
            newDelegate();////This  tempCount is 5 ;This  tempOtherCount is 7
            Console.Read();
      static MethodInvoker CreateDelegate()
        {
          int tempOtherCount = 5;
            MethodInvoker method = delegate
            {
                int tempCount = 5;
                Console.WriteLine("This  tempCount is  {0}", tempCount);
                Console.WriteLine("This  tempOtherCount is  {0}", tempOtherCount);
                tempCount++;
                tempOtherCount++;
            };
            method();//This  tempCount is 5 ;This  tempOtherCount is 5
            return method;
        }

  从上面的输出结果可以看出委托在捕捉变量的时候,要注意变量的位置,tempCount 和 我们在外部调用了两次委托实例,那相当于生成了两个不一样的tempCount实例,而tempOtherCount则是相同的一个实例,不同委托调用时,每个委托的tempCount实例是有区别的,而他们对tempOtherCount共用一个引用实例。在使用匿名方法的时候自己的小小疏忽可能会导致意想不到的结果。

  以上是对匿名方法捕捉变量的简单举例,在实际问题中,使用捕获变量这种方法可以简化代码。以下是捕获变量的规则:

  如果不用捕获变量的方法代码同样简单,就不用。

  在循环体中要注意你是用的委托是否需要在循环迭代结束以后延续,以及是否想让它继续使用那个被捕捉变量的后续值,否则的话,就在循环内另外建一个变量来复制你想要   的值。

  多委托时候要考虑是否这些委托要捕捉同一个变量。

  如果被捕捉的变量不会发生改变,就不需要这么多的担心。

  从垃圾回收的角度讲,思考任何捕获变量的被延长的生存期。这方面问题一般都不大,但是假如捕获的对象会产生昂贵的内存开销,这方面问题会凸现出来。(这点很重要)

 

 

  


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM