[深入學習C#]匿名函數、委託和Lambda運算式

來源:互聯網
上載者:User

標籤:c#   lambda   委託   匿名函數   

匿名函數

  匿名函數(Anonymous Function)是表示“內聯”方法定義的運算式。匿名函數本身及其內部沒有值或者類型,但是可以轉換為相容的委託或者運算式樹狀架構類型。匿名函數轉換的計算取決於轉換的目標類型:如果是委託類型,則轉換計算為引用匿名函數所定義的方法的委託;如果是運算式樹狀架構類型,則轉換將計算以對象結構形式表示方法結構的運算式樹狀架構。
  匿名函數有兩種文法風格:Lambda運算式(lambda-expression)和匿名方法運算式(anonymous-method-expression)。在幾乎所有的情況下,Lambda運算式都比匿名方法運算式更為簡介具有表現力。但現在C#語言中仍保留了後者,為了向後相容。
  Lambda運算式:
    async可選 (匿名的函數簽名)=> (匿名的函數體)
  匿名方法運算式:
    async可選 delegate (顯式的匿名函數簽名) 可選{代碼塊}

  其中匿名的函數簽名可以包括兩種,一種是隱式的匿名函數簽名另一種是顯式的匿名函數簽名:
    隱式的函數簽名:(p)、(p1,p1)
    顯式的函數簽名:(int p)、(int p1,int p2)、(ref int p1,out int p2)
  匿名的函數體可以是運算式或者代碼塊。

  從上面我們可以看出,Lambda運算式的參數形式可以顯式或者隱式類型化。在顯式類型化參數列表中,每個參數的類型是顯式聲明的,在隱式類型化參數列表中,參數的類型是從匿名函數出現的上下文中推斷出來的。
  當Lambda運算式只有一個具有隱式類型化參數的時候,參數列表可以省略圓括弧,也就是說:
  (參數) => 運算式
  可以簡寫為
  參數 => 運算式

一些匿名函數的樣本
  x => x + 1 //隱式的類型化,函數體為運算式
  x => {return x + 1;} //隱式的類型化,函數體為代碼塊
  (int x) => x + 1 //顯式的類型化,函數體為運算式
  (int x) => {return x + 1;} //顯式的類型化,函數體為代碼塊
  (x , y) => x * y //多參數
  () => Console.WriteLine() //無參數
  async (t1 , t2) => await t1 + await t2 //非同步
  delegate (int x) {return x + 1;} //匿名函數方法運算式
  delegate {return 1 + 1;} //參數列表省略
  
Lambda運算式和匿名方法運算式的區別:
  ● 當沒有參數的時候,匿名方法運算式允許完全省略參數列表,從而可以轉換為具有任意值參數列表的委託類型,Lambda運算式則不能省略參數列表的圓括弧()。
  ● Lambda運算式允許省略和推斷型別參數,而匿名方法運算式要求顯式聲明參數類型。
  ● Lambda運算式主體可以為運算式或者代碼塊,而匿名方法運算式的主體必須為代碼塊。
  ● 只有Lambda運算式可以相容到運算式樹狀架構類型。

委託

  一個委託是一個指向一個方法的引用,或者說,一個委託的執行個體就是一個指向某個方法的對象,這是一個簡單卻十分強大的概念。
  C#中的委託是用來處理在其他語言中(如C++、Pascal等)需要用函數指標來處理的情況。不過與C++不同的是:委託是完全物件導向的;C++指標僅僅指向成員函數,而委託同時封裝了對象的執行個體和方法;委託是完全型別安全的,只有當函數的簽名與委託的簽名匹配的時候,委託才可以指向該方法,當委託沒有合法的指向方法的時候不能被調用。

  一些關於委託的知識點:
  
  1.委託是型別安全的
  委託類型的傳回型別必須為void或者輸出安全,委託類型的所有形參類型都必須是輸入安全的。
  
  2.委託類型是名稱等效,不是結構等效
  也就是說,對於兩個委託類型,即使它們具有相同的參數列表和傳回型別,它們仍將被視為兩個不同的委託類型。
  例如:
  delegate int A(int x);
  delegate int B(int x);
  A和B是兩個不同的委託。
  
  但是,兩個結構一樣的委託,它們的執行個體可以指向同一個方法。
  
  3.委託的調用列表(多播委託)
  委託執行個體所封裝的方法集合稱為調用列表。
  當我們從某個方法建立一個委託執行個體的時候,該執行個體將封裝此方法,該執行個體中的調用列表包含一個“進入點”。當我們組合多個非空的委託執行個體的時候,它們的調用列表將串連在一起形成一個新的調用列表,新的調用列表中包含了多個“進入點”。
  委託的組合是使用二元運算子 + 和 += 來進行的,同樣可以使用 - 和 -= 來進行組合的移除。
  
  下面的樣本示範多個委託的執行個體化及其相應的調用列表:

delegate void D(int x)class{    public static void M1(int i){...}    public static void M2(int i){...}}class Test{    static void Main()    {        D cd1 = new D(c.M1);            //M1        D cd2 = new D(c.M2);            //M2        D cd3 = cd1 + cd2;              //M1 + M2        D cd4 = cd3 + cd1;              //M1 + M2 + M1        D cd5 = cd4 + cd3;              //M1 + M2 + M1 + M2    }}

  執行個體化cd1和cd2的時候,它們分別封裝一個方。執行個體化cd3的時候,它調用的列表有兩個方法M1和M2,而且順序與此相同。cd4的調用列表中依次包含M1、M2、M1。最後,cd5的調用列表中依次包含M2、M1、M1、M2。
  
  4.委託的調用
  當調用一個委託執行個體的時候,將按照調用列表的順序依次調用列表中的各個方法,當在調用期間發生異常,調用列表中排在後面的任何方法將不會被調用。

using System;delegate void D(int x);class C{    public static void M1(int i)    {        Console.WriteLine("C.M1:"+i);    }    public static void M2(int i)    {        Console.WriteLine("C.M1:"+i);    }      public  void M3(int i)    {        Console.WriteLine("C.M2:"+i);    }}class Test{    static void Main()    {        D cd1 = new D(c.M1);            //M1        cd1(-1);                        //調用cd1        D cd2 = new D(c.M2);            //M2        cd2(-2);                        //調用M2        D cd3 = cd1 + cd2;              //M1 + M2        cd3(10);                        //依次調用M1、M2        cd3 += cd1;        cd3(20);                        //依次調用M1、M2、M1        C c = new C();        D cd4 = new D(c.M3);                    cd3 += cd4;        cd3(30);                        //依次調用M1、M2、M1、M3        cd3 -= cd1                      //移除最後一個M1        cd3(40);                        //依次調用M1、M2、M3        cd3 -= cd4;                             cd3(50);                        //依次調用M1、M2        cd3 -= cd2        cd3(60);                        //調用M1        cd3 -= cd2                      //這是cd3的調用列表中沒有cd2了,該移除不生效,不報錯        cd3(70);                        //調用M1        cd3 -= cd1                              //      cd3(70);                //如果調用,將會報System.NullReferenceException異常    }}
Lambda運算式

  自從C#3.0開始,就可以使用一種新文法把實現代碼賦予委託:Lambda運算式。只要有委託參數類型的地方,就可以使用Lambda運算式。使用匿名方法的地方可以使用Lambda運算式來代替,例如:

Func< string,string > = delagate(string para){    para += "Hello World!";    return param;}  可以寫成Func< string,string > = para=>{    para +="Hello World!";    return para;}

  Lambda運算式運算子”=>”的左邊列出了需要的參數,右邊定義了賦予Lambda變數的方法的代碼實現。
  我們注意到,無論何時,只要我們需要引入匿名方法,我們都需要在前面加上delegate關鍵字,而且參數列表都需要類型化。Lambda運算式提供了一種更為簡潔和自然的文法,而且在C#更多進階的方面中都涉及廣泛。簡而言之,我們應該用Lambda運算式來替代匿名方法。
  例如:

public class Person{    string Name;    int Age;}List<Person> personList = new List<Person>();personList.Find(delegate(Person p){retrun p.Age==12;});//可以寫成personList.Find(p=>p.Age==12);

[深入學習C#]匿名函數、委託和Lambda運算式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.