Lambda
“Lambda 表達式”(lambda expression)是一個匿名函數,Lambda表達式基於數學中的λ演算得名,直接對應於其中的lambda抽象(lambda
abstraction),是一個匿名函數,即沒有函數名的函數。Lambda表達式可以表示閉包(注意和數學傳統意義上的不同)。
C#的Lambda 表達式都使用 Lambda
運算符 =>,該運算符讀爲“goes to”。語法如下:
形參列表=>函數體
函數體多於一條語句的可用大括號括起。
類型
可以將此表達式分配給委託類型,如下所示:
1
2
3
|
delegate int del( int i);
del myDelegate = x=>{ return x*x;};
int j = myDelegate(5);
|
創建表達式目錄樹類型:
1
2
3
|
using System.Linq.Expressions;
Expression <del>=x=>x*x;
|
=> 運算符具有與
賦值運算符 (=) 相同的優先級,並且是右結合運算符。
Lambda 用在基於方法的 LINQ 查詢中,作爲諸如 Where 和 Where 等標準查詢運算符方法的參數。
使用基於方法的語法在 Enumerable 類中調用 Where 方法時(像在 LINQ to Objects 和 LINQ to XML 中那樣),參數是委託類型 System..::.Func<(Of <(T, TResult>)>)。使用 Lambda 表達式創建委託最爲方便。例如,當您在 System.Linq..::.Queryable 類中調用相同的方法時(像在 LINQ to SQL 中那樣),則參數類型是 System.Linq.Expressions..::.Expression<Func>,其中
Func 是包含至多五個輸入參數的任何 Func 委託。同樣,Lambda 表達式只是一種用於構造表達式目錄樹的非常簡練的方式。儘管事實上通過 Lambda 創建的對象的類型是不同的,但 Lambda 使得 Where 調用看起來類似。
在前面的示例中,請注意委託簽名具有一個 int 類型的隱式類型輸入參數,並返回 int。可以將 Lambda 表達式轉換爲該類型的委託,因爲該表達式也具有一個輸入參數 (x),以及一個
編譯器可隱式轉換爲 int 類型的返回值。(以下幾節中將對類型推理進行詳細討論。)使用輸入參數
5
調用委託時,它將返回結果 25。
在 is 或 as 運算符的左側不允許使用 Lambda。
適用於
匿名方法的所有限制也適用於 Lambda 表達式。有關更多信息,請參見匿名方法(C# 編程指南)。
特殊
下列規則適用於 Lambda 表達式中的變量範圍:
捕獲的變量將不會被作爲垃圾回收,直至
引用變量的委託超出範圍爲止。
在外部方法中看不到 Lambda 表達式內引入的變量。
Lambda 表達式無法從封閉方法中直接捕獲 ref 或 out 參數。
Lambda 表達式中的返回語句不會導致封閉方法返回。
Lambda 表達式不能包含其目標位於所包含
匿名函數主體外部或內部的 goto 語句、break 語句或 continue 語句。
Lambda表達式的本質是“
匿名方法”,即當編譯我們的程序代碼時,“
編譯器”會自動將“Lambda表達式”轉換爲“匿名方法”,如下例:
1
2
3
|
string [] names={ "agen" , "balen" , "coure" , "apple" };
string [] findNameA=Array.FindAll< string >(names, delegate ( string v){ return v.StartsWith( "a" );});
string [] findNameB=Array.FindAll< string >(names,v=>v.StartsWith( "a" ));
|
上面中兩個FindAll方法的反編譯代碼如下:
1
2
|
string []findNameA=Array.FindAll< string >(names, delegate ( string v){ return v.StartsWith( "a" );});
string []findNameB=Array.FindAll< string >(names, delegate ( string v){ return v.StartsWith( "a" );});
|
從而可以知道“Lambda表達式”與“
匿名方法”是可以劃上等號的,只不過使用“Lambda表達式”輸寫代碼看上去更直觀漂亮,不是嗎?
Lambda表達式的語法格式:
參數列表 => 語句或語句塊
其中“參數列”中可包含任意個參數(與委託對應),如果參數列中有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”語句,
編譯器會自動處理,否則必須加上,如下示例:
“Lambda表達式”是委託的實現方法,所以必須遵循以下規則:
1)“Lambda表達式”的參數數量必須和“委託”的參數數量相同;
2)如果“委託”的參數中包括有ref或out
修飾符,則“Lambda表達式”的參數列中也必須包括有修飾符;
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Test
{
delegateintAddHandler(intx,inty);
staticvoidPrint(AddHandleradd)
{
Console.Write(add(1,3));
}
static void Main()
{
Print((x,y)=>x+y);
Print((x,y)=>{intv=x*10;returny+v;});
Console.Read();
}
}
|
注: 如果包括有
修飾符,則“Lambda表達式”中的參數列中也必須加上參數的類型
3)如果“委託”有返回類型,則“Lambda表達式”的“語句或語句塊”中也必須返回相同類型的數據;
4)如果“委託”有幾種數據類型格式而在“Lambda表達式”中“
編譯器”無法推斷具體數據類型時,則必須手動明確數據類型。
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
classTest
{
delegateTAddHandler<T>(Tx,Ty);
static void Print(AddHandler< int >test)
{
Console.WriteLine( "inttype:{0}" ,test(1,2));
}
static void Print(AddHandler< double >test)
{
Console.WriteLine( "doubletype:{0}" ,test(1d,2d));
}
staticvoidMain()
{
Print((x,y)=>x+y);
Console.Read();
}
}
|
當我們編譯以下代碼時,
編譯器將會顯示以下錯誤信息:
在以下方法或屬性之間的調用不明確:
“ConsoleApplication1.Test.Print(ConsoleApplication1.Test.AddHandler<int>)”和“ConsoleApplication1.Test.Print(ConsoleApplication1.Test.AddHandler<double>)”
這樣我們的代碼就能編譯通過了。