Lamda Expression

ambda表達式的書寫方式是一個參數列表後跟「=>」記號,而後跟一個表達式或一個語句塊,即Lambda表達式的語法格式爲:express

參數列 => 語句或語句塊函數

Lambda表達式例子以下所示:this

delegate int del(int i);spa

 ...element

 del myDelegate = x => x * x;編譯器

 int j = myDelegate(5); //j = 25it

關於「參數列」,Lambda表達式的參數列能夠具備顯式的或隱式的類型。在一個具備顯式類型的參數列表中,每一個參數的類型都是顯式聲明的。在一個具備隱式類型的參數列表中,參數的類型是從Lambda表達式出現的上下文中推斷出來的——具體來講,是當Lambda表達式被轉換爲一個兼容的委託類型時,該委託類型提供了參數的類型。io

當Lambda表達式只有一個具備隱式類型的參數時,參數列表中的括號能夠省略。即:編譯

(param) => exprclass

能夠簡寫爲:

param => expr

最後,參數列中可包含任意個參數(與委託對應),若是參數列中有0個或1個以上參數,則必須使用括號括住參數列,以下:

() => Console.Write("0個參數");

 i => Console.Write("1個參數時參數列中可省略括號,值爲:{0}", i);

 (x, y) => Console.Write("包含2個參數,值爲:{0}:{1}", x, y);

而「語句或語句塊」中若是隻有一條語句,則能夠不用大括號括住,不然則必須使用大括號,以下所示:

 i => Console.Write("只有一條語句");

 i => { Console.Write("使用大括號的表達式"); };

 //兩條語句時必需要大括號

i => { i++; Console.Write("兩條語句的狀況"); };

若是「語句或語句塊」有返回值時,若是隻有一條語句則能夠不寫「return」語句,編譯器會自動處理,不然必須加上,以下示例:

class Test

    {

        delegate int AddHandler(int x, int y);

        static void Print(AddHandler add)

        {

            Console.Write(add(1, 3));

        }

        static void Main()

        {

            Print((x, y) => x + y);

            Print((x, y) => { int v = x * 10; return y + v; });

            Console.Read();

        }

    }

Lambda表達式是委託的實現方法,因此必須遵循如下規則:

l         Lambda表達式的參數數量必須和委託的參數數量相同;

l         若是委託的參數中包括有ref或out修飾符,則Lambda表達式的參數列中也必須包括有修飾符;

咱們來看以下例子:

class Test

    {

        delegate void OutHandler(out int x);

        static void Print(OutHandler test)

        {

            int i;

            test(out i);

            Console.Write(i);

        }

        static void Main()

        {

            Print((out int x) => x = 3);

            Console.Read();

        }

    }

 

l         若是委託有返回類型,則Lambda表達式的語句或語句塊中也必須返回相同類型的數據;

l         若是委託有幾種數據類型格式而在Lambda表達式中編譯器沒法推斷具體數據類型時,則必須手動明確數據類型。

 

由上面可見,C# 2.0規範中提到的匿名方法規範一樣適用於Lambda表達式。Lambda表達式是匿名方法在功能行上的超集,提供了下列附加的功能:

l         Lambda表達式容許省略參數類型並對其進行推斷,而匿名方法要求參數類型必須顯式地聲明。

l         Lambda表達式體能夠是表達式或語句塊,而匿名方法體只能是語句塊。

l         在類型參數推導和方法重載抉擇時,Lambda表達式能夠被做爲參數傳遞。

l         以一個表達式做爲表達式體的Lambda表達式能夠被轉換爲表達式樹。

20.5.2 Lambda表達式轉換

和匿名方法表達式相似,Lambda表達式能夠歸類爲一種擁有特定轉換規則的值。這種值沒有類型,但能夠被隱式地轉換爲一個兼容的委託類型。特別地,當知足下列條件時,委託類型D兼容於Lambda表達式L:

l         D和L具備相同數量的參數;

l         若是L具備顯式類型的參數列表,D中每一個參數的類型和修飾符必須和L中相應的參數徹底一致;

l         若是L具備隱式類型的參數列表,則D中不能有ref或out參數;

l         若是D具備void返回值類型,而且L的表達式體是一個表達式,若L的每一個參數的類型與D的參數一致,則L的表達式體必須是一個可接受爲statement-expression的有效表達式;

l         若是D具備void返回值類型,而且L的表達式體是一個語句塊,若L的每一個參數的類型與D的參數一致,則L的表達式體必須是一個有效語句塊,而且該語句塊中不能有帶有表達式的return語句;

l         若是D的返回值類型不是void,而且L的表達式體是一個表達式,若L的每一個參數的類型與D的參數一致,則L的表達式體必須是一個能夠隱式轉換爲D的返回值類型的有效表達式;

l         若是D的返回值類型不是void,而且L的表達式體是一個語句塊,若L的每一個參數的類型與D的參數一致,則L的表達式體必須是一個有效的語句塊,該語句塊不能有可達的終點(即必須有return語句),而且每一個return語句中的表達式都必須可以隱式轉換爲D的返回值類型。

 

後面的例子將使用一個範型委託F<U, T>表示一個函數,它具備一個類型爲U的參數u,返回值類型爲T:

 

delegate T F<U, T>(U u);

 

咱們能夠像在下面這樣賦值:

 

F<int, int> f1 = x => x + 1;          

 F<int, double> f2 = x => x + 1;

 

每一個Lambda表達式的參數和返回值類型經過將Lambda表達式賦給的變量的類型來檢測。第一個賦值將Lambda表達式成功地轉換爲了委託類型Func<int, int>,由於x的類型是int,x + 1是一個有效的表達式,而且能夠被隱式地轉換爲int。一樣,第二個賦值成功地將Lambda表達式轉換爲了委託類型Func<int, double>,由於x + 1的結果(類型爲int)能夠被隱式地轉換爲double類型。

 

來看以下代碼,若是這樣賦值會怎麼樣?

F<double, int> f3 = x => x + 1;

咱們運行上面的代碼,編譯器會報以下兩條錯誤:

(1)沒法將類型「double」隱式轉換爲「int」。存在一個顯式轉換(是否缺乏強制轉換?)。

(2)沒法將Lambda表達式轉換爲委託類型「F<double,int>」,緣由是塊中的某些返回類型不能隱式轉換爲委託返回類型。

其實產生一個編譯期錯誤緣由是,x給定的類型是double,x + 1的結果(類型爲double)不能被隱式地轉換爲int。

20.5.3 類型推斷

當在沒有指定類型參數的狀況下調用一個範型方法時,一個類型推斷過程會去嘗試爲該調用推斷類型參數。被做爲參數傳遞給範型方法的Lambda表達式也會參與這個類型推斷過程。

最早發生的類型推斷獨立於全部參數。在這個初始階段,不會從做爲參數的Lambda表達式推斷出任何東西。然而,在初始階段以後,將經過一個迭代過程從Lambda表達式進行推斷。特別地,當下列條件之一爲真時將會完成推斷:

l         參數是一個Lambda表達式,之後簡稱爲L,從其中未獲得任何推斷;

l         相應參數的類型,之後簡稱爲P,是一個委託類型,其返回值類型包括了一個或多個方法類型參數;

l         P和L具備相同數量的參數,P中每一個參數的修飾符與L中相應的參數一致,或者若是L具備隱式類型的參數列表時,沒有參數修飾符;

l         P的參數類型不包含方法類型參數,或僅包含於已經推斷出來的類型參數相兼容的一組類型參數;

l         若是L具備顯式類型的參數列表,當推斷出來的類型被P中的方法類型參數取代了時,P中的每一個參數應該具備和L中相應參數一致的類型。

l         若是L具備隱式類型的參數列表,當推斷出來的類型被P中的方法類型參數取代了而且做爲結果的參數類型賦給了L時,L的表達式體必須是一個有效的表達式或語句塊。

l         能夠爲L推斷一個返回值類型。

 

對於每個這樣的參數,都是經過關聯P的返回值類型和從L推斷出的返回值類型來從其上進行推斷的,而且新的推斷將被添加到累積的推斷集合中。這個過程一直重複,直到沒法進行更多的推斷爲止。

在類型推斷和重載抉擇中,Lambda表達式L的「推斷出來的返回值類型」經過如下步驟進行檢測:

l         若是L的表達式體是一個表達式,則該表達式的類型就是L的推斷出來的返回值類型。

l         若是L的表達式體是一個語句塊,若由該塊中的return語句中的表達式的類型造成的集合中剛好包含一個類型,使得該集合中的每一個類型都能隱式地轉換爲該類型,而且該類型不是一個空類型,則該類型便是L的推斷出來的返回值類型。

l         除此以外,沒法從L推斷出一個返回值類型。

 

做爲包含了Lambda表達式的類型推斷的例子,請考慮System.Query.Sequence類中聲明的Select擴展方法:

 

namespace System.Query

    {

        public static class Sequence

        {

            public static IEnumerable<S> Select<T, S>(

                this IEnumerable<T> source,

                Func<T, S> selector)

            {

                foreach (T element in source) yield return selector(element);

            }

        }

    }

相關文章
相關標籤/搜索