lambda表達式和表達式樹(深入理解c#)
1.Lambda形式
1). Lambda表達式最冗長的形式:
(顯式類型的參數列表)=>{語句}
2). 大多數時候,都可以用一個表達式來表示主體,該表達式的值是Lambda的結果,在這些情況下,可以指定那個表達式,不使用大括號,不使用return語句,也不添加分號。
(顯式類型的參數列表)=> 表達式
3). 編譯器大多時候都能猜出參數類型,不需要你顯式聲明他們(隱式類型的參數列表就是一個以逗號分隔的名稱列表,沒有類型,但隱式和顯式類型的參數不能混合匹配——要麽全是隱式的,要麽全是顯式的)
(隱式類型的參數列表) => 表達式
4). 如果Lambda表達式只需一個參數,而且那個參數可以隱式指定類型,c#3允許省略圓括號。
參數名 => 表達式
2. 高階函數
Lambda表達式的主體本身可以包含另一個Lambda表達式,但做起來就像聽起來一樣,很容易讓人混淆,另外,Lambda表達式的參數可以是另一個委托,這樣做同樣很亂。
3. 表達式樹
.Net3.5的表達式樹提供了一種抽象方式將一些代碼表示成一個對象樹,c#3對於將Lambda表達式轉換成表達式樹提供了內建的支持。顧名思義,它們是對象構成的樹,樹中的每個節點本身就是一個表達式,不同的表達式類型代表能在代碼中執行的不同操作。
System.Linq。Expressions命名空間包含了代表表達式的各個類,它們都繼承自Expession,一個抽象的主要包含一些靜態工廠方法的類,這些方法用於創建其他表達式類的實例。然而Expression也包括兩個屬性
1)Type屬性代表表達式求值後的.Net類型,可把他視為一個返回了類型,例如,如果一個表達式要獲取一個字符串的Length屬性,該表達式的類型就是int。
2)NodeType屬性返回所代表的表達式的種類。他是ExpessionType枚舉的成員,包括LessThan,Multiply和Invoke等。仍然使用上面的例子,對於myString.Length這個屬性訪問來說,其節點類型是MemberAccess。該屬性最重要的地方,是它能區分由相同的類表示不同種類的表達式。
4. 將表達式樹編譯成委托
LambdaExpression是從Expression派生的類型。泛型類Expression<TDelegate>是從LambdaExpression派生的,其中泛型參數TDelegate必須是委托類型。
LambdaExpression有個Compile方法能創建恰當類型的一個委托。而Expression<TDelegate>的Compile方法返回TDelegate類型的委托。來看看下面的例子:
Expression<Func<int, int, int>> expr = (x, y) => x + y; ParameterExpression pex1 = Expression.Parameter(typeof(int), "x");//第一個參數 ParameterExpression pex2 = Expression.Parameter(typeof(int), "y");//第二個參數 BinaryExpression bexp = Expression.Add(pex1, pex2);//主體,加法 //使用Expression.Lambda方法,創建一個委托類型已知的Expression Expression<Func<int,int,int>> lambdaExp = Expression.Lambda<Func<int, int, int>>(bexp, new ParameterExpression[] { pex1, pex2 }); Func<int,int,int> tDelegate = lambdaExp.Compile();//編譯成委托 Console.WriteLine(tDelegate(1, 3)); Console.Read();
我們運行上面代碼,結果為:4。我們寫了一大堆代碼,本質上就是用表達式樹計算了1+3的結果。
5. 將c#Lambda表達式轉換成表達式樹
可以要求編譯器通過你的Lambda表達式構建一個表達式樹,在執行時創建Expression<TDelegate>的一個實例。
Expression<Func<int>> return5 = () => 5
限制:
並非所有Lambda表達式都能轉換成表達式樹。不能將帶有一個語句塊(即使只有一個return語句)的Lambda轉換成表達式樹——只對單個表達式進行求值的Lambda表達式才可以。表達式中還不能包含賦值操作,因為在表達式樹中表示不了這種操作。盡管.Net4擴展了表達式樹的功能,但只能轉換單一表達式這一限制仍然有效。(上述只是最常見的,還有很多)
代碼:
MethodInfo method = typeof(string).GetMethod("StartsWith", new[] {typeof(string) });
var target = Expression.Parameter( typeof(string) , "x");
var methodArg = Expression.Parameter( typeof(string) , "y");
Expression[] methodArgs = new[] {methodArg};
Expression call = Expression.Call( target,method,methodArgs);
var lambdaParamethers = new[] { target,methodArg};
var lambda = Expression.Lambda<Func<string,string,bool>>(call,lambdaParameters);
var compiled = lambda.Compile();
Console.WriteLine( compiled("First","Second" ));
Console.WriteLine( compiled("First","Fir" ));
lambda表達式和表達式樹(深入理解c#)