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