天天看點

匿名方法和Lambda表達式-天轟穿

=============================版權申明===============================

  任何人都可以轉載,但請注明轉載位址和作者“天轟穿”,因為下面這是我正在寫的 c# 這本書中的節選内容。

整本書都是這個風格,朋友們有啥期待盡管提。

===========================版權申明結束,内容開始======================

6.3.5 匿名方法

小天:我覺得多少還是有點死闆。有不有什麼辦法可以讓委托更靈活,比如某個階段我并不确定具體要調用的被委托方法名。或者根本不想專門為某個委托就寫一個冷僻的方法。

老田:可以使用匿名方法。在 2.0 之前的 C# 版本中,聲明委托的唯一方法是使用命名方法。C# 2.0 引入了匿名方法,而在 C# 3.0 及更高版本中,Lambda 表達式取代了匿名方法,作為編寫内聯代碼的首選方式。不過,本章讨論的這些内容也同樣也适用于 Lambda 表達式(稍後讨論)。有一種情況下,匿名方法提供了 Lambda表達式中所沒有的功能。匿名方法使您能夠省略參數清單,這意味着可以将匿名方法轉換為帶有各種簽名的委托。這對于 Lambda 表達式來說是不可能的。

要将代碼塊傳遞為委托參數,建立匿名方法則是唯一的方法。下面建立一個Windows窗體應用程式,在Form1的設計界面上拖一個button,然後在窗體空白處點右鍵,檢視代碼,在Form1的構造函數中編寫代碼,為button1的click關聯一個匿名方法作為執行體,具體Form1的構造函數代碼如下:

        public Form1()

        {

            InitializeComponent();

            button1.Click += delegate(Object o, EventArgs e)

            {

                //匿名事件中的代碼

                MessageBox.Show("按鈕事件!");

            }; //注意大括号後面有一個分号作為結束

        }

然後運作程式。會發現,點選按鈕執行的事件就是我們上面寫的這個;

小天:匿名方法一定要跟上面示例中這兩個參數嗎?

老田:當然不是,上面示例之是以要跟這兩個參數是因為button的click委托規定了接受他委托的方法的要求是這樣的。否則我們也可以像下面這樣:

            //自定義一個委托類型(注意申明委托的位置,錯了别怪我)

            delegate void Del(int x);

            //申明一個Del委托類型的執行個體,并為他關聯一個匿名類型的匿名事件

            Del d = delegate(int k) {  };

名方法的優點是減少了要編寫的代碼。不必定義僅由委托使用的方法。在為事件定義委托時,這是非常顯然的。這有助于降低代碼的複雜性,尤其是定義了好幾個事件時,代碼會顯得比較簡單。使用匿名方法時,代碼執行得不太快。編譯器仍定義了一個方法,該方法隻有一個自動指定的名稱,我們不需要知道這個名稱。

在使用匿名方法時,必須遵循兩個規則。在匿名方法中不能使用跳轉語句跳到該匿名方法的外部,反之亦然:匿名方法外部的跳轉語句不能跳到該匿名方法的内部。

在匿名方法内部不能通路不安全的代碼。另外,也不能通路在匿名方法外部使用的ref和out參數。但可以使用在匿名方法外部定義的其他變量。

如果需要用匿名方法多次編寫同一個功能,就不要使用匿名方法。而編寫一個指定的方法比較好,因為該方法隻需編寫一次,以後可通過名稱引用它。

6.3.6  Lambda表達式

C# 3.0為匿名方法提供了一個新的文法:Lambda表達式。Lambda表達式可以用于委托類型。“Lambda表達式”是一個匿名函數,它可以包含表達式和語句,并且可用于建立委托或表達式目錄樹類型。

所有 Lambda 表達式都使用 Lambda 運算符 =>,該運算符讀為“goes to”。該 Lambda 運算符的左邊是輸入參數(如果有),右邊包含表達式或語句塊。Lambda 表達式 x => x * x 讀作“x goes to x * x”。可以将此表達式配置設定給委托類型,如下所示:

    class Program

    {

        delegate int del(int i);

        static void Main(string[] args)

        {

            del myDelegate = x => x * x;

            int j = myDelegate(5); //j的值為調用委托所得到的值

            Console.WriteLine(j);   //值為25

            Console.ReadLine();

        }

    }

現在基本上明白了Lambda表達式的寫法,接下來我們結合匿名方法來做一個執行個體:

    class Program

    {

        delegate int del(int i);

        static void Main(string[] args)

        {

            del myDelegate = x => x * x;

            int j = myDelegate(5); //j的值為調用委托所得到的值

            Console.WriteLine(j);   //值為25

            //注意這裡result實際上是del委托類的參數,是以一定是int類型

            del d1 = result =>    

                {

                    result += 10;   //對傳入的result參數做簡單處理

                    return result; //傳回結果

                }; //注意大括号後面有一個分号作為結束

            Console.WriteLine(d1(2));   //直接調用委托,結果顯示12

            Console.ReadLine();

        }

    }

老田:給你布置個任務,自定義一個多個參數的委托類型,結合Lambda表達式和匿名方法來做個執行個體。

小天:等等先問兩個問題:

1.                  為什麼你上面第一個例題 del myDelegate = x => x * x;  這段代碼中 => 符号後面的x * x 沒有給大括号,但是第二個是給的大括号?

2.                  多個參數在 => 符号前面怎麼表示?

老田:第一個問題:如同if語句一樣,如果下面隻有一行代碼,則可以省略大括号;例如下面這樣也是對的

            del myDelegate = x =>{return  x * x};

第二個問題:多個參數任然寫在=> 符号前面,不過需要用括号括起來,同時多個參數之間用逗号分隔,例如

Del d1 = (x, y, z) => z + y + z;

小天:那就簡單了。如下,我定義了一個名為XiaoTian的委托,兩個參數,然後在匿名方法中對參數進行了簡單的處理,最後傳回這裡我還做了點舉一反三,如下:

    class Program

    {

        //三個參數的委托

        delegate string XiaoTian(string xr, int age, string sex);

        static void Main(string[] args)

        {

            //多個參數就必須使用括号,參數之間用逗号分隔

            XiaoTian xt = (xr, age, sex) =>

                {

                    StringBuilder jg = new StringBuilder();

                    jg.Append("小天此人今年");

                    jg.Append(age);

                    jg.Append("歲,性别");

                    jg.Append(sex);

                    jg.Append(",長相");

                    jg.Append(xr);

                    return jg.ToString();   //直接傳回結果

                };

            //調用委托

            Console.WriteLine(xt("帥得一塌糊塗", 28, "男"));

            Console.ReadLine();

        }

    }

運作後效果如圖6-6

                                                 圖6-6

老田:不錯。上面我們讨論的都是預定義參數的情況下,那麼假設傳入的參數類型是自定義的類型,恰恰編譯器推斷不出來怎麼辦呢?那麼接下來就要說到的是在為參數指定類型,如下:

    //自定義一個有兩個屬性的類

    class Thc

    {

        public Thc(string n, int a)

        {

            Name = n;

            Age = a;

        }

        public string Name { get; set; }

        public int Age { get; set; }

}

//控制台應用程式的起始類

   class Program

    {

        //使用自定義類型的參數

        delegate string More(Thc t);

        static void Main(string[] args)

        {

            //對參數申明類型

            More m =(Thc t )=>

                {

                    return "姓名為" + t.Name + ",年齡" + t.Age;

                };

            //調用委托,注意所給的參數是新執行個體一個對象

            Console.WriteLine(m(new Thc("天轟穿", 30)));

            Console.ReadLine();

        }

    }

       小天:上面說了這麼多都是有參數的,如果遇上沒有參數的咋辦?

       老田:沒有參數更省事,直接給個空的括号就行,如下:

    class Program

    {

        //無參數

        delegate void Wucanshu();

        static void Main(string[] args)

        {

            //對于無參數的情況就直接給一個空的括号就行了

            Wucanshu wcs = () =>

            {

                Console.WriteLine( "我就是無參數,你把我咋樣嘛!");

            };

            //調用委托

            wcs();

            Console.ReadLine();

        }

    }

繼續閱讀