(Disclaimer:如果需要轉載請先與我聯(lián)系;文中圖片請不要直接鏈接
作者:RednaxelaFX at rednaxelafx.javaeye.com)
系列文章:
LINQ與DLR的Expression tree(1):簡(jiǎn)介L(cháng)INQ與Expression treeLINQ與DLR的Expression tree(2):簡(jiǎn)介DLRLINQ與DLR的Expression tree(3):LINQ與DLR的樹(shù)的對比LINQ與DLR的Expression tree(4):創(chuàng )建靜態(tài)類(lèi)型的LINQ表達式樹(shù)節點(diǎn)
LINQ與DLR的Expression tree(5):用lambda表達式表示常見(jiàn)控制結構系列的前三篇介紹過(guò)LINQ與DLR中的expressiontree的概況后,我們對LINQ與DLR所使用的AST的概況和關(guān)系應該有一定認識了。既然LINQ Expression tree現在是DLRtree的子集,而DLR能夠使用DLR tree來(lái)支持動(dòng)態(tài)類(lèi)型的語(yǔ)言,那么LINQ Expressiontree能不能同樣支持動(dòng)態(tài)類(lèi)型方式的呢?系列接下來(lái)的文章就會(huì )圍繞這個(gè)主題,描繪一個(gè)從使用LINQ Expressiontree過(guò)渡到使用DLR tree的過(guò)程。
本篇,我們先來(lái)詳細看看要自己手工創(chuàng )建一棵LINQ Expression tree大概是個(gè)什么樣子。
System.Linq.Expressions.Expression類(lèi)上的工廠(chǎng)方法在第一篇介紹LINQ Expressiontree的時(shí)候提到過(guò),創(chuàng )建節點(diǎn)主要是通過(guò)調用Expression類(lèi)上的靜態(tài)工廠(chǎng)方法,而不是直接使用具體的Expression類(lèi)的構造器來(lái)完成的。下面直接用代碼來(lái)演示C#中的一些基本表達式結構是如何用Expressiontree來(lái)表示的,并在代碼最后的Main()方法里演示一個(gè)稍微復雜一點(diǎn)的表達式的構造。
由于代碼比較長(cháng),下面將分段把代碼貼出來(lái)。如果用IE瀏覽提示“腳本可能失去響應”,請不要擔心,并選擇繼續執行腳本。本文的完整代碼可以通過(guò)附件下載。
常量表達式部分:
沒(méi)什么值得解釋的,就是通過(guò)Expression.Constant()來(lái)表示一個(gè)常量。注意一個(gè)常量可以是C#里的字面量,如0、1、2.0、"abc"、null等,也可以是任意別的語(yǔ)義上符合“常量”概念的對象。
-
-
- using System;
- using System.Collections.Generic;
- using System.Linq.Expressions;
-
- static class ExpressionTreeSamples {
-
- #region Constant
-
-
- static void Constant( ) {
-
- Expression<Func<int>> con = Expression.Lambda<Func<int>>(
- Expression.Constant(
- 1,
- typeof( int )
- ),
- new ParameterExpression[ ] { }
- );
- }
-
- #endregion
// By RednaxelaFX, 2008-09-07using System;using System.Collections.Generic;using System.Linq.Expressions;static class ExpressionTreeSamples {#region Constant// Represents a constant (such as a literal)static void Constant( ) {// Expression<Func<int>> con = ( ) => 1;Expression<Func<int>> con = Expression.Lambda<Func<int>>(Expression.Constant(1, // valuetypeof( int ) // type),new ParameterExpression[ ] { });}#endregion算術(shù)表達式部分:
這部分代碼演示了+、-、*、/、%、冪、一元-、一元+等運算符在Expression tree中的對應物。需要說(shuō)明的地方有五點(diǎn):
1、.NET中算術(shù)運算可以?huà)伋鯫verflowException來(lái)表示運算結果溢出(overflow)了,也就是超過(guò)了數據類(lèi)型所能表示的范圍。用戶(hù)可以選擇不理會(huì )這些異常(
unchecked)或者關(guān)注這些異常(
checked)。C#中默認是unchecked??梢詫σ粋€(gè)表達式或者一個(gè)語(yǔ)句塊指定checked與unchecked。與此相應,Expression tree API中也對算術(shù)運算(和類(lèi)型轉換運算)提供了checked與unchecked的兩個(gè)版本。
2、在第二個(gè)Add()的例子里,原本的lambda表達式只接受一個(gè)參數(y),但在表達式內容中使用了一個(gè)自由變量(x)。這個(gè)時(shí)候如果是由C#編譯器來(lái)編譯,就會(huì )自動(dòng)生成一個(gè)私有內部類(lèi)(這里用CompilerGeneratedDisplayClass來(lái)模擬),并將自由變量x變?yōu)槠涑蓡T變量。主要是想通過(guò)這個(gè)例子來(lái)演示自己如何通過(guò)創(chuàng )建類(lèi)似的類(lèi)來(lái)模擬lambda表達式對自由變量的捕獲(也就是閉包的功能)。
3、Divide()與DivideDouble()實(shí)際上演示的都是Expression.Divide()的使用。特意用兩個(gè)例子來(lái)演示是為了說(shuō)明無(wú)論是整型除法還是浮點(diǎn)數除法,Expression.Divide()都能正確處理。Expressiontree中的其它算術(shù)表達式也是同理。
4、C#中沒(méi)有冪運算符,所以Power()中的lambda表達式是用VB.NET來(lái)寫(xiě)的;VB.NET有冪運算符,“^”,不過(guò)實(shí)際上也是映射到Math.Pow()上的調用而已。
5、UnaryPlus()中演示的lambda表達式實(shí)際被C#編譯器編譯時(shí)是不會(huì )生成對Expression.UnaryPlus()的調用的,因為這個(gè)運算符作用在int上并沒(méi)有什么意義,只是直接返回其操作數的值而已。但是Expression treeAPI允許用戶(hù)指定在創(chuàng )建這些算術(shù)運算表達式節點(diǎn)時(shí)使用什么方法來(lái)執行運算,當用戶(hù)選擇使用默認以外的方法時(shí),這個(gè)表達式還是可能有不同的意義的。例如這個(gè)Expression.UnaryPlus有兩個(gè)版本的重載:
默認方法:
- public static UnaryExpression UnaryPlus( Expression expression )
public static UnaryExpression UnaryPlus( Expression expression )
用戶(hù)指定方法:
- public static UnaryExpression UnaryPlus( Expression expression, MethodInfo method )
public static UnaryExpression UnaryPlus( Expression expression, MethodInfo method )
采用第二個(gè)版本時(shí),根據以下規則判斷使用什么方法來(lái)實(shí)現一元加:
引用
- 如果 method 不為 null 引用(在 Visual Basic 中為 Nothing),并且它表示帶一個(gè)參數的非 void static(在 Visual Basic 中為 Shared)方法,則它是節點(diǎn)的實(shí)現方法。
- 如果 expression.Type 為定義一元正運算符的用戶(hù)定義的類(lèi)型,則表示此運算符的 MethodInfo 為實(shí)現方法。
- 否則,如果 expression.Type 為數值類(lèi)型,則實(shí)現方法為 null 引用(在 Visual Basic 中為 Nothing)。
- #region Arithmetic
-
-
- static void Add( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> add = Expression.Lambda<Func<int, int, int>>(
- Expression.Add(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- private class CompilerGeneratedDisplayClass {
- public int x;
- }
-
- static void Add( int x ) {
-
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- var display = new CompilerGeneratedDisplayClass( );
- display.x = x;
- Expression<Func<int, int>> add = Expression.Lambda<Func<int, int>>(
- Expression.Add(
- Expression.Field(
- Expression.Constant( display ),
- typeof( CompilerGeneratedDisplayClass ).GetField( "x" )
- ),
- y
- ),
- new ParameterExpression[ ] { y }
- );
- }
-
-
- static void AddChecked( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> add = Expression.Lambda<Func<int, int, int>>(
- Expression.AddChecked(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void Subtract( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> sub = Expression.Lambda<Func<int, int, int>>(
- Expression.Subtract(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void SubtractChecked( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> sub = Expression.Lambda<Func<int, int, int>>(
- Expression.SubtractChecked(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void Multiply( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> mul = Expression.Lambda<Func<int, int, int>>(
- Expression.Multiply(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void MultiplyChecked( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> mul = Expression.Lambda<Func<int, int, int>>(
- Expression.MultiplyChecked(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void Divide( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> div = Expression.Lambda<Func<int, int, int>>(
- Expression.Divide(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void DivideDouble( ) {
-
-
-
-
-
-
- ParameterExpression x = Expression.Parameter( typeof( double ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( double ), "y" );
- Expression<Func<double, double, double>> div = Expression.Lambda<Func<double, double, double>>(
- Expression.Divide(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void Modulo( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> mod = Expression.Lambda<Func<int, int, int>>(
- Expression.Modulo(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void Power( ) {
-
-
-
-
-
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> pow = Expression.Lambda<Func<int, int, int>>(
- Expression.Power(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void Negate( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- Expression<Func<int, int>> neg = Expression.Lambda<Func<int, int>>(
- Expression.Negate(
- x
- ),
- new ParameterExpression[ ] { x }
- );
- }
-
-
- static void NegateChecked( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- Expression<Func<int, int>> neg = Expression.Lambda<Func<int, int>>(
- Expression.NegateChecked(
- x
- ),
- new ParameterExpression[ ] { x }
- );
- }
-
-
- static void UnaryPlus( ) {
-
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- Expression<Func<int, int>> unaryPlus = Expression.Lambda<Func<int, int>>(
- Expression.UnaryPlus(
- x
- ),
- new ParameterExpression[ ] { x }
- );
- }
-
- #endregion
#region Arithmetic// "+" operatorstatic void Add( ) {// Expression<Func<int, int, int>> add = ( x, y ) => x + y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> add = Expression.Lambda<Func<int, int, int>>(Expression.Add(x, // lefty // right),new ParameterExpression[ ] { x, y });}// simulates a compiler generated closure classprivate class CompilerGeneratedDisplayClass {public int x;}static void Add( int x ) {// Expression<Func<int, int>> add = y => x + y;ParameterExpression y = Expression.Parameter( typeof( int ), "y" );var display = new CompilerGeneratedDisplayClass( );display.x = x;Expression<Func<int, int>> add = Expression.Lambda<Func<int, int>>(Expression.Add(Expression.Field(Expression.Constant( display ),typeof( CompilerGeneratedDisplayClass ).GetField( "x" )),y),new ParameterExpression[ ] { y });}// "+" operator, checkedstatic void AddChecked( ) {// Expression<Func<int, int, int>> add = ( x, y ) => checked( x + y );ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> add = Expression.Lambda<Func<int, int, int>>(Expression.AddChecked(x, // lefty // right),new ParameterExpression[ ] { x, y });}// "-" operatorstatic void Subtract( ) {// Expression<Func<int, int, int>> sub = ( x, y ) => x - y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> sub = Expression.Lambda<Func<int, int, int>>(Expression.Subtract(x, // lefty // right),new ParameterExpression[ ] { x, y });}// "-" operator, checkedstatic void SubtractChecked( ) {// Expression<Func<int, int, int>> sub = ( x, y ) => checked( x - y );ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> sub = Expression.Lambda<Func<int, int, int>>(Expression.SubtractChecked(x, // lefty // right),new ParameterExpression[ ] { x, y });}// "*" operatorstatic void Multiply( ) {// Expression<Func<int, int, int>> mul = ( x, y ) => x * y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> mul = Expression.Lambda<Func<int, int, int>>(Expression.Multiply(x, // lefty // right),new ParameterExpression[ ] { x, y });}// "*" operator, checkedstatic void MultiplyChecked( ) {// Expression<Func<int, int, int>> mul = ( x, y ) => checked( x * y );ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> mul = Expression.Lambda<Func<int, int, int>>(Expression.MultiplyChecked(x, // lefty // right),new ParameterExpression[ ] { x, y });}// "/" operator (demonstrates integral division)static void Divide( ) {// Expression<Func<int, int, int>> div = ( x, y ) => x / y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> div = Expression.Lambda<Func<int, int, int>>(Expression.Divide(x, // lefty // right),new ParameterExpression[ ] { x, y });}// // "+" operator (demonstrates floating-point division)static void DivideDouble( ) {// Note that this expression tree has exactly the same shape as the one// in Divide(). The only difference is the type of parameters and result.// The expression tree can find correct implementation of the divide operator// in both cases.// Expression<Func<double, double, double>> div = ( x, y ) => x / y;ParameterExpression x = Expression.Parameter( typeof( double ), "x" );ParameterExpression y = Expression.Parameter( typeof( double ), "y" );Expression<Func<double, double, double>> div = Expression.Lambda<Func<double, double, double>>(Expression.Divide(x, // lefty // right),new ParameterExpression[ ] { x, y });}// "%" operatorstatic void Modulo( ) {// Expression<Func<int, int, int>> mod = ( x, y ) => x % y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> mod = Expression.Lambda<Func<int, int, int>>(Expression.Modulo(x, // lefty // right),new ParameterExpression[ ] { x, y });}// exponentiation operator (not available in C#)static void Power( ) {// There‘s no "raise to the power" operator in C#, but there is one// in VB.NET, the "^" operator.// So this sample is in VB9:// Dim pow As Expression(Of Func(Of Integer, Integer, Integer)) _// = Function( x, y ) x ^ yParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> pow = Expression.Lambda<Func<int, int, int>>(Expression.Power(x, // lefty // right),new ParameterExpression[ ] { x, y });}// unary "-" operatorstatic void Negate( ) {// Expression<Func<int, int>> neg = x => -x;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );Expression<Func<int, int>> neg = Expression.Lambda<Func<int, int>>(Expression.Negate(x // expression),new ParameterExpression[ ] { x });}// unary "-" operator, checkedstatic void NegateChecked( ) {// Expression<Func<int, int>> neg = x => checked( -x );ParameterExpression x = Expression.Parameter( typeof( int ), "x" );Expression<Func<int, int>> neg = Expression.Lambda<Func<int, int>>(Expression.NegateChecked(x // expression),new ParameterExpression[ ] { x });}// unary "+" operatorstatic void UnaryPlus( ) {// Note that C# compiler will optimize this by removing the unary plus//Expression<Func<int, int>> unary = x => +x;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );Expression<Func<int, int>> unaryPlus = Expression.Lambda<Func<int, int>>(Expression.UnaryPlus(x // expression),new ParameterExpression[ ] { x });}#endregion按位運算表達式部分:
有兩點(diǎn)需要說(shuō)明:
1、C#中(以及.NET Framework的基礎類(lèi)庫(BCL)中沒(méi)有邏輯右移運算符“>>>”,而Java、JavaScript等語(yǔ)言中是存在這個(gè)運算符的。
2、千萬(wàn)要注意,
Expression.And()與
Expression.Or()是表示按位運算用的,而
Expression.AndAlso()與
Expression.OrElse()才是表示邏輯運算的。
- #region Bitwise
-
-
- static void LeftShift( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> lshift = Expression.Lambda<Func<int, int, int>>(
- Expression.LeftShift(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void RightShift( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> expression = Expression.Lambda<Func<int, int, int>>(
- Expression.RightShift(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
-
-
-
- }
-
-
- static void And( ) {
-
-
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> and = Expression.Lambda<Func<int, int, int>>(
- Expression.And(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void Or( ) {
-
-
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> or = Expression.Lambda<Func<int, int, int>>(
- Expression.Or(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void ExclusiveOr( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, int>> xor = Expression.Lambda<Func<int, int, int>>(
- Expression.ExclusiveOr(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
- #endregion
#region Bitwise// "<<" operatorstatic void LeftShift( ) {// Expression<Func<int, int, int>> lshift = ( x, y ) => x << y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> lshift = Expression.Lambda<Func<int, int, int>>(Expression.LeftShift(x, // lefty // right),new ParameterExpression[ ] { x, y });}// ">>" operatorstatic void RightShift( ) {// Expression<Func<int, int, int>> rshift = ( x, y ) => x >> y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> expression = Expression.Lambda<Func<int, int, int>>(Expression.RightShift(x, // lefty // right),new ParameterExpression[ ] { x, y });// Note that C# doesn‘t have a logical right shift operator ">>>",// neither is there one in the expression tree}// "&" operatorstatic void And( ) {// Note that And() is for bitwise and, and AndAlso() is for logical and.// Expression<Func<int, int, int>> and = ( x, y ) => x & y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> and = Expression.Lambda<Func<int, int, int>>(Expression.And(x, // lefty // right),new ParameterExpression[ ] { x, y });}// "|" operatorstatic void Or( ) {// Note that Or() is for bitwise or, and OrElse() is for logical or.// Expression<Func<int, int, int>> or = ( x, y ) => x | y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> or = Expression.Lambda<Func<int, int, int>>(Expression.Or(x,y),new ParameterExpression[ ] { x, y });}// "^" operatorstatic void ExclusiveOr( ) {// Expression<Func<int, int, int>> xor = ( x, y ) => x ^ y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, int>> xor = Expression.Lambda<Func<int, int, int>>(Expression.ExclusiveOr(x, // lefty // right),new ParameterExpression[ ] { x, y });}#endregion條件表達式部分:
也就是C-like語(yǔ)言中常見(jiàn)的三元運算符“? :”。注意這個(gè)對應的不是if-else
語(yǔ)句,而是條件
表達式——C-like語(yǔ)言中表達式有值,語(yǔ)句不一定有(即便有值也被忽略了);條件表達式的兩個(gè)分支的值的類(lèi)型必須匹配。
- #region Conditional
-
-
- static void Condition( ) {
-
- ParameterExpression c = Expression.Parameter( typeof( bool ), "c" );
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<bool, int, int, int>> cond = Expression.Lambda<Func<bool, int, int, int>>(
- Expression.Condition(
- c,
- x,
- y
- ),
- new ParameterExpression[ ] { c, x, y }
- );
- }
-
- #endregion
#region Conditional// "? :" operatorstatic void Condition( ) {// Expression<Func<bool, int, int, int>> cond = ( c, x, y ) => c ? x : y;ParameterExpression c = Expression.Parameter( typeof( bool ), "c" );ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<bool, int, int, int>> cond = Expression.Lambda<Func<bool, int, int, int>>(Expression.Condition(c, // testx, // if truey // if false),new ParameterExpression[ ] { c, x, y });}#endregion相等性表達式部分:
也就是等于大于小于等比較大小的部分。比較直觀(guān),不多說(shuō)。
- #region Equality
-
-
- static void Equal( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, bool>> eq = Expression.Lambda<Func<int, int, bool>>(
- Expression.Equal(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void NotEqual( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, bool>> neq = Expression.Lambda<Func<int, int, bool>>(
- Expression.NotEqual(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
-
-
-
- }
-
-
- static void GreaterThan( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, bool>> gt = Expression.Lambda<Func<int, int, bool>>(
- Expression.GreaterThan(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void GreaterThanOrEqual( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, bool>> ge = Expression.Lambda<Func<int, int, bool>>(
- Expression.GreaterThanOrEqual(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void LessThan( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, bool>> lt = Expression.Lambda<Func<int, int, bool>>(
- Expression.LessThan(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void LessThanOrEqual( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( int ), "y" );
- Expression<Func<int, int, bool>> le = Expression.Lambda<Func<int, int, bool>>(
- Expression.LessThanOrEqual(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
- #endregion
#region Equality// "==" operatorstatic void Equal( ) {// Expression<Func<int, int, bool>> eq = ( x, y ) => x == y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, bool>> eq = Expression.Lambda<Func<int, int, bool>>(Expression.Equal(x, // lefty // right),new ParameterExpression[ ] { x, y });}// "!=" operatorstatic void NotEqual( ) {// Expression<Func<int, int, bool>> neq = ( x, y ) => x != y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, bool>> neq = Expression.Lambda<Func<int, int, bool>>(Expression.NotEqual(x, // lefty // right),new ParameterExpression[ ] { x, y });// Note that the lambda above isn‘t equivalent to the following:// Expression<Func<int, int, bool>> neq2 = ( x, y ) => !( x == y );}// ">" operatorstatic void GreaterThan( ) {// Expression<Func<int, int, bool>> gt = ( x, y ) => x > y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, bool>> gt = Expression.Lambda<Func<int, int, bool>>(Expression.GreaterThan(x, // lefty // right),new ParameterExpression[ ] { x, y });}// ">=" operatorstatic void GreaterThanOrEqual( ) {// Expression<Func<int, int, bool>> ge = ( x, y ) => x >= y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, bool>> ge = Expression.Lambda<Func<int, int, bool>>(Expression.GreaterThanOrEqual(x, // lefty // right),new ParameterExpression[ ] { x, y });}// "<" operatorstatic void LessThan( ) {// Expression<Func<int, int, bool>> lt = ( x, y ) => x < y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, bool>> lt = Expression.Lambda<Func<int, int, bool>>(Expression.LessThan(x, // lefty // right),new ParameterExpression[ ] { x, y });}// "<=" operatorstatic void LessThanOrEqual( ) {// Expression<Func<int, int, bool>> le = ( x, y ) => x <= y;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );ParameterExpression y = Expression.Parameter( typeof( int ), "y" );Expression<Func<int, int, bool>> le = Expression.Lambda<Func<int, int, bool>>(Expression.LessThanOrEqual(x, // lefty // right),new ParameterExpression[ ] { x, y });}#endregion關(guān)系表達式部分:
基本上只要注意好與前面按位運算的And()、Or()區分開(kāi)就行。另外需要記住的是,Expression.AndAlso()與Expression.OrElse()的默認語(yǔ)義與C#/C++中的&&、||一樣,是短路表達式——會(huì )根據對左操作數求值的結果決定是否對右操作數求值。
- #region Relational
-
-
- static void AndAlso( ) {
-
-
-
-
- ParameterExpression x = Expression.Parameter( typeof( bool ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( bool ), "y" );
- Expression<Func<bool, bool, bool>> and = Expression.Lambda<Func<bool, bool, bool>>(
- Expression.AndAlso(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void OrElse( ) {
-
-
-
-
- ParameterExpression x = Expression.Parameter( typeof( bool ), "x" );
- ParameterExpression y = Expression.Parameter( typeof( bool ), "y" );
- Expression<Func<bool, bool, bool>> or = Expression.Lambda<Func<bool, bool, bool>>(
- Expression.OrElse(
- x,
- y
- ),
- new ParameterExpression[ ] { x, y }
- );
- }
-
-
- static void Not( ) {
-
- ParameterExpression b = Expression.Parameter( typeof( bool ), "b" );
- Expression<Func<bool, bool>> not = Expression.Lambda<Func<bool, bool>>(
- Expression.Not(
- b
- ),
- new ParameterExpression[ ] { b }
- );
- }
-
- #endregion
#region Relational// "&&" operatorstatic void AndAlso( ) {// Note that And() is for bitwise and, and AndAlso() is for logical and.// Note also that the shortcut semantics is implemented with AndAlso().// Expression<Func<bool, bool, bool>> and = ( x, y ) => x && y;ParameterExpression x = Expression.Parameter( typeof( bool ), "x" );ParameterExpression y = Expression.Parameter( typeof( bool ), "y" );Expression<Func<bool, bool, bool>> and = Expression.Lambda<Func<bool, bool, bool>>(Expression.AndAlso(x, // lefty // right),new ParameterExpression[ ] { x, y });}// "||" operatorstatic void OrElse( ) {// Note that Or() is for bitwise or, and OrElse() is for logical or.// Note also that the shortcut semantics is implemented with OrElse().// Expression<Func<bool, bool, bool>> or = ( x, y ) => x || y;ParameterExpression x = Expression.Parameter( typeof( bool ), "x" );ParameterExpression y = Expression.Parameter( typeof( bool ), "y" );Expression<Func<bool, bool, bool>> or = Expression.Lambda<Func<bool, bool, bool>>(Expression.OrElse(x, // lefty // right),new ParameterExpression[ ] { x, y });}// "!" operatorstatic void Not( ) {// Expression<Func<bool, bool>> or = b => !b;ParameterExpression b = Expression.Parameter( typeof( bool ), "b" );Expression<Func<bool, bool>> not = Expression.Lambda<Func<bool, bool>>(Expression.Not(b // expression),new ParameterExpression[ ] { b });}#endregion類(lèi)型轉換表達式部分:
注意點(diǎn)就在于.NET中的值類(lèi)型(value type)只能用C-style的類(lèi)型轉換,所以也只能對應Expression.Convert();而引用類(lèi)型(reference type)既可以用C-style的也可以用
as運算符來(lái)做類(lèi)型轉換,可以根據需要選用Expression.Convert()或者Expression.TypeAs()。
對了,對C#不熟悉的人可能對
??運算符感到陌生。這個(gè)運算符的語(yǔ)義是:當左操作數不為null時(shí),該表達式的值為左操作數的值;反之則為右操作數的值。
- #region Type Conversion
-
-
- static void Convert( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- Expression<Func<int, short>> conv = Expression.Lambda<Func<int, short>>(
- Expression.Convert(
- x,
- typeof( short )
- ),
- new ParameterExpression[ ] { x }
- );
- }
-
-
- static void ConvertChecked( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int ), "x" );
- Expression<Func<int, short>> conv = Expression.Lambda<Func<int, short>>(
- Expression.ConvertChecked(
- x,
- typeof( short )
- ),
- new ParameterExpression[ ] { x }
- );
- }
-
-
- static void TypeAs( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( string ), "x" );
- Expression<Func<string, object>> typeAs = Expression.Lambda<Func<string, object>>(
- Expression.TypeAs(
- x,
- typeof( object )
- ),
- new ParameterExpression[ ] { x }
- );
- }
-
-
- static void TypeIs( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( string ), "x" );
- Expression<Func<string, bool>> typeIs = Expression.Lambda<Func<string, bool>>(
- Expression.TypeIs(
- x,
- typeof( object )
- ),
- new ParameterExpression[ ] { x }
- );
- }
-
-
- static void Coalesce( ) {
-
- ParameterExpression x = Expression.Parameter( typeof( int? ), "x" );
- Expression<Func<int?, int>> coal = Expression.Lambda<Func<int?, int>>(
- Expression.Coalesce(
- x,
- Expression.Constant( 1, typeof( int ) )
- ),
- new ParameterExpression[ ] { x }
- );
- }
-
- #endregion
#region Type Conversion// C-style conversionstatic void Convert( ) {// Expression<Func<int, short>> conv = x => ( short ) x;ParameterExpression x = Expression.Parameter( typeof( int ), "x" );Expression<Func<int, short>> conv = Expression.Lambda<Func<int, short>>(Expression.Convert(x, // expressiontypeof( short ) // type),new ParameterExpression[ ] { x });}// C-style conversion, checkedstatic void ConvertChecked( ) {// Expression<Func<int, short>> conv = x => checked( ( short ) x );ParameterExpression x = Expression.Parameter( typeof( int ), "x" );Expression<Func<int, short>> conv = Expression.Lambda<Func<int, short>>(Expression.ConvertChecked(x, // expressiontypeof( short ) // type),new ParameterExpression[ ] { x });}// "as" operatorstatic void TypeAs( ) {// Expression<Func<string, object>> typeAs = x => x as object;ParameterExpression x = Expression.Parameter( typeof( string ), "x" );Expression<Func<string, object>> typeAs = Expression.Lambda<Func<string, object>>(Expression.TypeAs(x, // expressiontypeof( object ) // type),new ParameterExpression[ ] { x });}// "is" operatorstatic void TypeIs( ) {// Expression<Func<string, bool>> typeIs = x => x is object;ParameterExpression x = Expression.Parameter( typeof( string ), "x" );Expression<Func<string, bool>> typeIs = Expression.Lambda<Func<string, bool>>(Expression.TypeIs(x, // expressiontypeof( object ) // type),new ParameterExpression[ ] { x });}// "??" operatorstatic void Coalesce( ) {// Expression<Func<int?, int>> coal = x => x ?? 1;ParameterExpression x = Expression.Parameter( typeof( int? ), "x" );Expression<Func<int?, int>> coal = Expression.Lambda<Func<int?, int>>(Expression.Coalesce(x, // leftExpression.Constant( 1, typeof( int ) ) // right),new ParameterExpression[ ] { x });}#endregion成員表達式部分:
這里的“成員”主要是指域(field)和屬性(property)了。
在演示Expression.Field()的時(shí)候我用了前面模擬編譯器生成的內部類(lèi),只是圖個(gè)方便而已,因為實(shí)在想不到.NET基礎類(lèi)庫里(特別是System命名空間里)有什么類(lèi)型會(huì )包含共有域并且該域不是常量的。Int32.MaxValue是一個(gè)公有域,但同時(shí)也是個(gè)常量,C#編譯器一編譯就把lambda表達式里的域訪(fǎng)問(wèn)優(yōu)化成一個(gè)常量了。所以干脆用一個(gè)自定義的類(lèi)來(lái)演示域訪(fǎng)問(wèn)表達式。
Expression treeAPI里除了Expression.Field()和Expression.Property(),還有一個(gè)Expression.PropertyOrField()工廠(chǎng)方法。但我沒(méi)想出來(lái)在什么情況下無(wú)法得知一個(gè)名字到底是成員域還是成員屬性,所以沒(méi)寫(xiě)這個(gè)對應的例子。
- #region Member
-
-
- static void Field( ) {
-
-
-
- ParameterExpression c = Expression.Parameter(
- typeof( CompilerGeneratedDisplayClass ), "c" );
- Expression<Func<CompilerGeneratedDisplayClass, int>> field =
- Expression.Lambda<Func<CompilerGeneratedDisplayClass, int>>(
- Expression.Field(
- c,
- "x"
- ),
- new ParameterExpression[ ] { c }
- );
- }
-
-
- static void Property( ) {
-
- ParameterExpression s = Expression.Parameter( typeof( string ), "s" );
- Expression<Func<string, int>> prop = Expression.Lambda<Func<string, int>>(
- Expression.Property(
- s,
- typeof( string ).GetProperty( "Length" )
- ),
- new ParameterExpression[ ] { s }
- );
- }
-
- #endregion
#region Member// Accessing a fieldstatic void Field( ) {// Reusing the CompilerGeneratedDisplayClass class for demo.//Expression<Func<CompilerGeneratedDisplayClass, int>> field = c => c.x;ParameterExpression c = Expression.Parameter(typeof( CompilerGeneratedDisplayClass ), "c" );Expression<Func<CompilerGeneratedDisplayClass, int>> field =Expression.Lambda<Func<CompilerGeneratedDisplayClass, int>>(Expression.Field(c,"x"),new ParameterExpression[ ] { c });}// Calling a propertystatic void Property( ) {// Expression<Func<string, int>> prop = s => s.Length;ParameterExpression s = Expression.Parameter( typeof( string ), "s" );Expression<Func<string, int>> prop = Expression.Lambda<Func<string, int>>(Expression.Property(s, // expressiontypeof( string ).GetProperty( "Length" ) // property),new ParameterExpression[ ] { s });}#endregion方法/委托調用表達式部分:
有兩點(diǎn)需要注意:
1、Expression.Call()對應的是方法調用,而Expression.Invoke()對應的是委托(delegate)的調用。它們在.NET中最大的不同可以說(shuō)是:一個(gè)委托背后的方法可能是某個(gè)類(lèi)上的方法,也可能是一個(gè)通過(guò)LCG(lightweight codegeneration)生成的方法;后者是不與任何類(lèi)相關(guān)聯(lián)的,所以調用機制會(huì )有點(diǎn)區別。在Expression treeAPI中這個(gè)區別就體現為工廠(chǎng)方法的不同。
2、Expression.Call()既對應成員方法的調用,也對應靜態(tài)方法的調用。調用靜態(tài)方法時(shí)把instance參數設為null。
- #region Invocation
-
-
- static void Call( ) {
-
-
-
-
- ParameterExpression s = Expression.Parameter( typeof( string ), "s" );
- Expression<Func<string, int>> scall = Expression.Lambda<Func<string, int>>(
- Expression.Call(
- null,
- typeof( int ).GetMethod(
- "Parse", new Type[ ] { typeof( string ) } ),
- new Expression[ ] { s }
- ),
- new ParameterExpression[ ] { s }
- );
- }
-
-
- static void CallMember( ) {
-
-
-
-
- ParameterExpression s = Expression.Parameter( typeof( string ), "s" );
- Expression<Func<string, string>> mcall = Expression.Lambda<Func<string, string>>(
- Expression.Call(
- s,
- typeof( string ).GetMethod(
- "ToUpper", new Type[ ] { } ),
- new Expression[ ] { }
- ),
- new ParameterExpression[ ] { s }
- );
- }
-
-
- static void Invoke( ) {
-
-
-
-
- ParameterExpression f = Expression.Parameter( typeof( Func<int> ), "f" );
- Expression<Func<Func<int>, int>> invoc = Expression.Lambda<Func<Func<int>, int>>(
- Expression.Invoke(
- f,
- new Expression[ ] { }
- ),
- new ParameterExpression[ ] { f }
- );
- }
-
- #endregion
#region Invocation// Calling a static methodstatic void Call( ) {// Note that to call a static method, use Expression.Call(),// and set "instance" to null// Expression<Func<string, int>> scall = s => int.Parse( s );ParameterExpression s = Expression.Parameter( typeof( string ), "s" );Expression<Func<string, int>> scall = Expression.Lambda<Func<string, int>>(Expression.Call(null, // instancetypeof( int ).GetMethod( // method"Parse", new Type[ ] { typeof( string ) } ),new Expression[ ] { s } // arguments),new ParameterExpression[ ] { s });}// Calling a member methodstatic void CallMember( ) {// Note that to call a member method, use Expression.Call(),// and set "instance" to the expression for the instance.// Expression<Func<string, string>> mcall = s => s.ToUpper( );ParameterExpression s = Expression.Parameter( typeof( string ), "s" );Expression<Func<string, string>> mcall = Expression.Lambda<Func<string, string>>(Expression.Call(s, // instancetypeof( string ).GetMethod( // method"ToUpper", new Type[ ] { } ),new Expression[ ] { } // arguments),new ParameterExpression[ ] { s });}// Invoking a delegatestatic void Invoke( ) {// Note that invoking a delegate is different from calling a method.// Use Expression.Invoke() instead of Expression.Call().// Expression<Func<Func<int>, int>> invoc = f => f( );ParameterExpression f = Expression.Parameter( typeof( Func<int> ), "f" );Expression<Func<Func<int>, int>> invoc = Expression.Lambda<Func<Func<int>, int>>(Expression.Invoke(f, // expressionnew Expression[ ] { } // arguments),new ParameterExpression[ ] { f });}#endregion數組表達式部分:
就是對數組下標和數組長(cháng)度的特殊處理,沒(méi)什么特別需要注意的。
- #region Array
-
-
- static void ArrayIndex( ) {
-
- ParameterExpression a = Expression.Parameter( typeof( int[ ] ), "a" );
- ParameterExpression i = Expression.Parameter( typeof( int ), "i" );
- Expression<Func<int[ ], int, int>> aryIdx = Expression.Lambda<Func<int[ ], int, int>>(
- Expression.ArrayIndex(
- a,
- i
- ),
- new ParameterExpression[ ] { a, i }
- );
- }
-
-
- static void ArrayLength( ) {
-
- ParameterExpression a = Expression.Parameter( typeof( int[ ] ), "a" );
- Expression<Func<int[ ], int>> aryLen = Expression.Lambda<Func<int[ ], int>>(
- Expression.ArrayLength(
- a
- ),
- new ParameterExpression[ ] { a }
- );
- }
-
- #endregion
#region Array// Array index expression ("[]" operator on arrays)static void ArrayIndex( ) {// Expression<Func<int[ ], int, int>> aryIdx = ( a, i ) => a[ i ];ParameterExpression a = Expression.Parameter( typeof( int[ ] ), "a" );ParameterExpression i = Expression.Parameter( typeof( int ), "i" );Expression<Func<int[ ], int, int>> aryIdx = Expression.Lambda<Func<int[ ], int, int>>(Expression.ArrayIndex(a, // arrayi // index),new ParameterExpression[ ] { a, i });}// Array length expression (".Length" property on arrays)static void ArrayLength( ) {// Expression<Func<int[ ], int>> aryLen = a => a.Length;ParameterExpression a = Expression.Parameter( typeof( int[ ] ), "a" );Expression<Func<int[ ], int>> aryLen = Expression.Lambda<Func<int[ ], int>>(Expression.ArrayLength(a // array),new ParameterExpression[ ] { a });}#endregion新建對象表達式部分:
基本上就是對應“new”運算符相關(guān)的表達式了。這部分有兩點(diǎn)需要注意:
1、與數組相關(guān)的兩個(gè)初始化方法:
Expression.NewArrayBounds()接受的參數是數組的元素類(lèi)型和數組的尺寸。尺寸可以是一維或多維的,取決于指定數組尺寸的表達式的個(gè)數。下面的例子里演示的是一個(gè)二維數組。注意到它是一個(gè)矩形數組而不是一個(gè)“數組的數組”(array-of-array,或者叫jagged array)。
Expression.NewArrayInit()方法對應的是數字的初始化器,例如new int[] { 1, 2, 3 };它接受的參數是數組的元素類(lèi)型和由初始化表達式組成的數組。
2、Expression.ListInit()并不只是能創(chuàng )建和初始化System.Collections.Generic.List<T>類(lèi)型的對象。任何能用C#3.0的列表初始化器表示的新建對象表達式都能用Expression.ListInit()表示,例如這個(gè):
- var hashset = new HashSet<string> {
- "Alpha", "Beta", "Charlie"
- };
var hashset = new HashSet<string> {"Alpha", "Beta", "Charlie"};要理解這個(gè)初始化器實(shí)際上被編譯器轉換為對默認構造器與一系列Add()方法的調用。在使用Expression.ListInit()時(shí)這些實(shí)際調用必須手工指定。下面的例子很明顯:先選擇了默認構造器并用一個(gè)Expression.New()來(lái)調用,然后是一組通過(guò)Expression.ElementInit()對Add()方法的調用。能夠制定調用的方法自然意味著(zhù)可以使用Add()以外的方法作為初始化器的內容咯。
- #region New
-
-
- static void New( ) {
-
- Expression<Func<object>> newObj = Expression.Lambda<Func<object>>(
- Expression.New(
- typeof( object ).GetConstructor( new Type[ ] { } ),
- new Expression[ ] { }
- ),
- new ParameterExpression[ ] { }
- );
- }
-
-
- static void NewArrayBounds( ) {
-
- Expression<Func<int[ , ]>> newArr = Expression.Lambda<Func<int[ , ]>>(
- Expression.NewArrayBounds(
- typeof( int ),
- new Expression[ ] {
- Expression.Constant( 1, typeof( int ) ),
- Expression.Constant( 2, typeof( int ) )
- }
- ),
- new ParameterExpression[ ] { }
- );
- }
-
-
- static void NewArrayInit( ) {
-
- Expression<Func<int[ ]>> newArrInit = Expression.Lambda<Func<int[ ]>>(
- Expression.NewArrayInit(
- typeof( int ),
- new Expression[ ] { }
- ),
- new ParameterExpression[ ] { }
- );
- }
-
-
- static void ListInit( ) {
-
- Expression<Func<List<int>>> linit = Expression.Lambda<Func<List<int>>>(
- Expression.ListInit(
- Expression.New(
- typeof( List<int> ).GetConstructor( new Type[ ] { } ),
- new Expression[ ] { }
- ),
- new ElementInit[ ] {
- Expression.ElementInit(
- typeof( List<int> ).GetMethod( "Add", new Type[ ] { typeof( int ) } ),
- new Expression[ ] { Expression.Constant( 1, typeof( int ) ) }
- ),
- Expression.ElementInit(
- typeof( List<int> ).GetMethod( "Add", new Type[ ] { typeof( int ) } ),
- new Expression[ ] { Expression.Constant( 2, typeof( int ) ) }
- )
- }
- ),
- new ParameterExpression[ ] { }
- );
- }
-
- #endregion
#region New// Creating a new object instancestatic void New( ) {// Expression<Func<object>> n = ( ) => new object( );Expression<Func<object>> newObj = Expression.Lambda<Func<object>>(Expression.New(typeof( object ).GetConstructor( new Type[ ] { } ), // constructornew Expression[ ] { } // arguments),new ParameterExpression[ ] { });}// Creating a new array with specified boundsstatic void NewArrayBounds( ) {// Expression<Func<int[ , ]>> n = ( ) => new int[ 1, 2 ];Expression<Func<int[ , ]>> newArr = Expression.Lambda<Func<int[ , ]>>(Expression.NewArrayBounds(typeof( int ), // typenew Expression[ ] { // boundsExpression.Constant( 1, typeof( int ) ),Expression.Constant( 2, typeof( int ) )}),new ParameterExpression[ ] { });}// Creating a new array with initializersstatic void NewArrayInit( ) {// Expression<Func<int[ ]>> n = ( ) => new int[ ] { };Expression<Func<int[ ]>> newArrInit = Expression.Lambda<Func<int[ ]>>(Expression.NewArrayInit(typeof( int ), // typenew Expression[ ] { } // initializers),new ParameterExpression[ ] { });}// Creating a new list with initializersstatic void ListInit( ) {// Expression<Func<List<int>>> n = ( ) => new List<int>( ) { 1, 2 };Expression<Func<List<int>>> linit = Expression.Lambda<Func<List<int>>>(Expression.ListInit(Expression.New( // new expressiontypeof( List<int> ).GetConstructor( new Type[ ] { } ),new Expression[ ] { }),new ElementInit[ ] { // initializersExpression.ElementInit(typeof( List<int> ).GetMethod( "Add", new Type[ ] { typeof( int ) } ),new Expression[ ] { Expression.Constant( 1, typeof( int ) ) }),Expression.ElementInit(typeof( List<int> ).GetMethod( "Add", new Type[ ] { typeof( int ) } ),new Expression[ ] { Expression.Constant( 2, typeof( int ) ) })}),new ParameterExpression[ ] { });}#endregion成員初始化表達式部分:
對應物是C# 3.0中的成員初始化器;最常見(jiàn)的地方莫過(guò)于匿名類(lèi)型的實(shí)例的聲明了,例如:
- new { FistName = "John", LastName = "Smith" }
new { FistName = "John", LastName = "Smith" }為了演示方便,這里建了一個(gè)簡(jiǎn)單的內部類(lèi)。
- #region Member Initialization
-
-
- private class DummyClass {
- public int Value { get; set; }
- }
-
-
- static void MemberInit( ) {
-
- Expression<Func<DummyClass>> minit = Expression.Lambda<Func<DummyClass>>(
- Expression.MemberInit(
- Expression.New(
- typeof( DummyClass ).GetConstructor( new Type[ ] { } ),
- new Expression[ ] { }
- ),
- new MemberBinding[ ] {
- Expression.Bind(
- typeof( DummyClass ).GetProperty( "Value" ),
- Expression.Constant( 2, typeof( int ) )
- )
- }
- ),
- new ParameterExpression[ ] { }
- );
- }
-
- #endregion
#region Member Initialization// simulates an anonymous typeprivate class DummyClass {public int Value { get; set; }}// Creating a new object instance with member initializersstatic void MemberInit( ) {// Expression<Func<DummyClass>> memInit = ( ) => new { Value = 2 };Expression<Func<DummyClass>> minit = Expression.Lambda<Func<DummyClass>>(Expression.MemberInit(Expression.New(typeof( DummyClass ).GetConstructor( new Type[ ] { } ),new Expression[ ] { }),new MemberBinding[ ] {Expression.Bind(typeof( DummyClass ).GetProperty( "Value" ),Expression.Constant( 2, typeof( int ) ))}),new ParameterExpression[ ] { });}#endregion“引用”表達式部分:
這對于許多C#程序員來(lái)說(shuō)或許不是那么直觀(guān)的內容,不過(guò)如果了解Lisp的話(huà)就很好解釋。Expression.Quote()與Lisp中的quote特殊形式作用相似,作用是不對其操作數求值,而是將其操作數表達式作為結果的值。聽(tīng)起來(lái)有點(diǎn)拗口,我也說(shuō)不太清楚,或許舉個(gè)例子會(huì )容易理解一些。
假如我有一個(gè)lambda表達式,
- Func<int> five = ( ) => 2 + 3;
Func<int> five = ( ) => 2 + 3;
然后調用five(),應該得到結果5。
但假如C#里有一種神奇的運算符(這里讓我用反單引號來(lái)表示),能夠阻止表達式的求值,那么下面的lambda表達式:
- Func<Expression> fiveExpr = ( ) = `( 2 + 3 );
Func<Expression> fiveExpr = ( ) = `( 2 + 3 );
對fiveExpr()調用后得到的就是表示2+3的BinaryExpression。
這個(gè)Expression.Quote()所達到的效果也正是這一點(diǎn)。
-
- static void Quote( ) {
-
-
-
-
-
-
-
-
-
- Expression<Func<BinaryExpression>> quote = Expression.Lambda<Func<BinaryExpression>>(
- Expression.Quote(
- Expression.Add(
- Expression.Constant( 2.0 ),
- Expression.Constant( 3.0 )
- )
- ),
- new ParameterExpression[ ] { }
- );
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
// Quoting an Expressionstatic void Quote( ) {// There‘s no equivalent syntax for quoting an Expression in C#.// The quote UnaryExpression is used for preventing the evaluation// of its operand expression. Semantically, this results in// returning its operand as the result of this unary expression,// as opposed to returning the evaluated value of its operand.//// It‘s rather like the quote special form in Lisp, where// <code>(quote datum)</code> evaluates to <code>datum</code>.Expression<Func<BinaryExpression>> quote = Expression.Lambda<Func<BinaryExpression>>(Expression.Quote(Expression.Add(Expression.Constant( 2.0 ),Expression.Constant( 3.0 ))),new ParameterExpression[ ] { });// Func<BinaryExpression> quteFunc = quote.Compile( );// Console.WriteLine( quoteFunc( ) ); // prints (2 + 3)//// In contrast, a normal Expression.Add() without the quote// will return 4 instead://Expression<Func<int>> add = Expression.Lambda<Func<int>>(// Expression.Add(// Expression.Constant( 2.0 ),// Expression.Constant( 3.0 )// ),// new ParameterExpression[ ] { }//);//Func<int> addFunc = add.Compile( );//Console.WriteLine( addFunc( ) ); // prints 5}未說(shuō)明的工廠(chǎng)方法:
這幾個(gè)工廠(chǎng)方法主要是用于自定義的表達式的,沒(méi)有C#的直觀(guān)的對應物,所以就不在這里說(shuō)明了。
// not listed above://PropertyOrField : MemberExpression//MakeBinary : BinaryExpression//MakeMemberAccess : MemberExpression//MakeUnary : UnaryExpression
Main()方法。演示一個(gè)稍微復雜一點(diǎn)的表達式:
這段代碼的解釋放到代碼后。請先閱讀代碼看看:
- static void Main( string[ ] args ) {
-
-
-
-
-
-
- ParameterExpression array = Expression.Parameter( typeof( int[ ] ), "array" );
- Expression<Func<int[ ], int>> complexExpr = Expression.Lambda<Func<int[ ], int>>(
- Expression.Condition(
- Expression.AndAlso(
- Expression.NotEqual(
- array,
- Expression.Constant(
- null
- )
- ),
- Expression.NotEqual(
- Expression.ArrayLength(
- array
- ),
- Expression.Constant(
- 0,
- typeof( int )
- )
- )
- ),
- Expression.Multiply(
- Expression.Constant(
- 5,
- typeof( int )
- ),
- Expression.Add(
- Expression.Constant(
- 10,
- typeof( int )
- ),
- Expression.ArrayIndex(
- array,
- Expression.Constant(
- 0,
- typeof( int )
- )
- )
- )
- ),
- Expression.Constant(
- -1,
- typeof( int )
- )
- ),
- new ParameterExpression[ ] { array }
- );
-
-
- Func<int[ ], int> func = complexExpr.Compile( );
- int[ ] arrayArg = new[ ] { 2, 3, 4, 5 };
- Console.WriteLine( func( arrayArg ) );
- }
- }
static void Main( string[ ] args ) {// Let‘s make a little complex expression tree sample,// equivalent to://Expression<Func<int[ ], int>> complexExpr =// array => ( array != null && array.Length != 0 ) ?// 5 * ( 10 + array[ 0 ] ) :// -1;ParameterExpression array = Expression.Parameter( typeof( int[ ] ), "array" );Expression<Func<int[ ], int>> complexExpr = Expression.Lambda<Func<int[ ], int>>(Expression.Condition(Expression.AndAlso(Expression.NotEqual(array,Expression.Constant(null)),Expression.NotEqual(Expression.ArrayLength(array),Expression.Constant(0,typeof( int )))),Expression.Multiply(Expression.Constant(5,typeof( int )),Expression.Add(Expression.Constant(10,typeof( int )),Expression.ArrayIndex(array,Expression.Constant(0,typeof( int ))))),Expression.Constant(-1,typeof( int ))),new ParameterExpression[ ] { array });// And let‘s see it in action:Func<int[ ], int> func = complexExpr.Compile( );int[ ] arrayArg = new[ ] { 2, 3, 4, 5 };Console.WriteLine( func( arrayArg ) ); // prints 60}}Main()方法里,我們手工構造了一個(gè)由基本表達式組裝起來(lái)的表達式。當然,我是可以把其中的各個(gè)子表達式先分別賦值給變量,避免這種巨大的嵌套調用;不過(guò)嵌套調用也有好處,那就是能比較直觀(guān)的與“樹(shù)形”聯(lián)系起來(lái)?;仡欉@個(gè)lambda表達式:
- Expression<Func<int[ ], int>> complexExpr =
- array => ( array != null && array.Length != 0 ) ?
- 5 * ( 10 + array[ 0 ] ) :
- -1;
Expression<Func<int[ ], int>> complexExpr =array => ( array != null && array.Length != 0 ) ?5 * ( 10 + array[ 0 ] ) :-1;
它對應的抽象語(yǔ)法樹(shù)(AST)如下圖所示?;貞浧餉ST的特征,注意到用于改變表達式優(yōu)先順序的括號已經(jīng)不需要了:
這個(gè)AST與實(shí)際對Expression上的工廠(chǎng)方法的調用是一一對應的:
(圖片縮小了的話(huà)請點(diǎn)擊放大)
不難看出Expression tree與C#的表達式的關(guān)系。
Expression tree的Compile()方法的一個(gè)注意點(diǎn)還是先看一個(gè)例子。我們要寫(xiě)一個(gè)比較奇怪的lambda表達式,并讓它變成一棵Expression tree:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
-
- static class Program {
- static void Main( string[ ] args ) {
- Expression<Func<List<int>, IEnumerable<int>>> filter =
- list => from i in list
- where i % 2 == 0
- select i;
- Console.WriteLine( filter );
- var fooList = new List<int> { 1, 2, 3, 4, 5 };
- var result = filter.Compile( )( fooList );
- foreach ( var i in result ) {
- Console.WriteLine( i );
- }
- }
- }
using System;using System.Collections.Generic;using System.Linq;using System.Linq.Expressions;static class Program {static void Main( string[ ] args ) {Expression<Func<List<int>, IEnumerable<int>>> filter =list => from i in listwhere i % 2 == 0select i;Console.WriteLine( filter );var fooList = new List<int> { 1, 2, 3, 4, 5 };var result = filter.Compile( )( fooList );foreach ( var i in result ) {Console.WriteLine( i );}}}運行得到:
引用
list => list.Where(i => ((i % 2) = 0))
2
4
注意到那個(gè)查詢(xún)表達式(from...where...select...)會(huì )被翻譯為擴展方法的調用,所以上面代碼里的filter等價(jià)于:
- list => list.Where( i => i % 2 == 0 )
list => list.Where( i => i % 2 == 0 )
也就是說(shuō)lambda表達式里面還嵌套有lambda表達式。
另外注意到List<T>實(shí)現了IEnumerable<T>而沒(méi)有實(shí)現IQueryable<T>,所以這個(gè)Where()方法是Enumerable類(lèi)上的。也就是說(shuō)filter等價(jià)于:
- list => Enumerable.Where<int>( list, i => i % 2 == 0 )
list => Enumerable.Where<int>( list, i => i % 2 == 0 )
然后讓我們把這棵Expression tree改用手工方式創(chuàng )建:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
-
- static class Program {
- static void Main( string[ ] args ) {
- ParameterExpression list = Expression.Parameter(typeof(List<int>), "list");
- ParameterExpression i = Expression.Parameter(typeof(int), "i");
- Expression<Func<List<int>, IEnumerable<int>>> filter = Expression.Lambda<Func<List<int>, IEnumerable<int>>>(
- Expression.Call(
- typeof( Enumerable ).GetMethods( )
- .First( method => "Where" == method.Name
- && 2 == method.GetParameters( ).Length )
- .MakeGenericMethod( new [ ] { typeof( int ) } ),
- new Expression[ ] {
- list,
- Expression.Lambda<Func<int, bool>>(
- Expression.Equal(
- Expression.Modulo(
- i,
- Expression.Constant(
- 2,
- typeof(int)
- )
- ),
- Expression.Constant(
- 0,
- typeof(int)
- )
- ),
- new [ ] { i }
- )
- }
- ),
- new [ ] { list }
- );
- Console.WriteLine( filter );
- var fooList = new List<int> { 1, 2, 3, 4, 5 };
- var result = filter.Compile( )( fooList );
- foreach ( var item in result )
- Console.WriteLine( item );
- }
- }
using System;using System.Collections.Generic;using System.Linq;using System.Linq.Expressions;static class Program {static void Main( string[ ] args ) {ParameterExpression list = Expression.Parameter(typeof(List<int>), "list");ParameterExpression i = Expression.Parameter(typeof(int), "i");Expression<Func<List<int>, IEnumerable<int>>> filter = Expression.Lambda<Func<List<int>, IEnumerable<int>>>(Expression.Call(typeof( Enumerable ).GetMethods( ).First( method => "Where" == method.Name&& 2 == method.GetParameters( ).Length ).MakeGenericMethod( new [ ] { typeof( int ) } ),new Expression[ ] {list,Expression.Lambda<Func<int, bool>>(Expression.Equal(Expression.Modulo(i,Expression.Constant(2,typeof(int))),Expression.Constant(0,typeof(int))),new [ ] { i })}),new [ ] { list });Console.WriteLine( filter );var fooList = new List<int> { 1, 2, 3, 4, 5 };var result = filter.Compile( )( fooList );foreach ( var item in result )Console.WriteLine( item );}}留意一下我是如何通過(guò)反射來(lái)得到Enumerable.Where<TSource>>(thisIEnumerable<TSource> source, Func<TSource, bool>predicate)這個(gè)方法的MethodInfo的。由于Enumerable.Where()有兩個(gè)重載,而且都是publicstatic的,我們無(wú)法直接調用Type.GetMethod(string)來(lái)得到MethodInfo;另外,也無(wú)法通過(guò)Type.GetMethod(string,Type[])來(lái)得到這個(gè)MethodInfo,因為它是泛型方法定義的MethodInfo,不方便直接指定類(lèi)型。結果只好通過(guò)參數個(gè)數來(lái)區分兩個(gè)Enumerable.Where(),然后創(chuàng )建出Enumerable.Where<int>()的MethodInfo。
注意到,Enumerable.Where<int>()的第二個(gè)參數是Func<int, bool>類(lèi)型的,但我提供的參數數組里的卻是Expression<Func<int, bool>>類(lèi)型的。這樣能匹配么?
判斷標準很簡(jiǎn)單,只要參數數組里各項的Expression.Type與實(shí)際參數類(lèi)型匹配就能行。在調用Compile()的時(shí)候,即使有嵌套的lambda表達式(轉換成Expression tree也就是Expression<TDelegate>)也沒(méi)問(wèn)題。
Expression<Func<int, bool>>的Type屬性返回的就是Func<int, bool>,與Where()需要的參數類(lèi)型匹配,OK。
沒(méi)注意到的話(huà)一開(kāi)始可能會(huì )被轉暈掉,所以我覺(jué)得值得一提。
LINQ Expression tree與靜態(tài)類(lèi)型LINQ Expression tree是靜態(tài)類(lèi)型的,上面的例子里我都選用了顯式指定類(lèi)型的版本的API來(lái)創(chuàng )建樹(shù)的節點(diǎn)(特別是Expression.Constant(),其實(shí)不指定類(lèi)型也可以的)。
雖然Expression tree的
內容是靜態(tài)類(lèi)型的,但仔細觀(guān)察會(huì )發(fā)現組裝表達式時(shí),參數類(lèi)型基本上都只要求是Expression或其派生類(lèi)就行,無(wú)法避免在組裝時(shí)把不匹配的表達式錯誤的組裝到一起。為什么會(huì )這樣呢?
對編譯器有所了解的人應該很清楚,把源代碼解析到抽象語(yǔ)法樹(shù)只是完成了語(yǔ)法分析,之后還需要做語(yǔ)義分析。語(yǔ)義分析就包括了類(lèi)型檢查等工作。表達式的一個(gè)重要特征就是可組合性,一個(gè)表達式的子表達式可以是任意語(yǔ)法結構的表達式;類(lèi)型的匹配與否無(wú)法通過(guò)語(yǔ)法來(lái)表現。當我們把一個(gè)lambda表達式賦值給一個(gè)Expressin<TDelegate>類(lèi)型的變量時(shí),C#編譯器做了類(lèi)型檢查,并且生成了相應的Expressiontree。但當我們手工創(chuàng )建Expressiontree時(shí),我們既獲得了創(chuàng )建并組裝節點(diǎn)的權利,也承擔起了檢查類(lèi)型的義務(wù)——C#編譯器只知道那些是表示表達式的對象,而無(wú)法進(jìn)一步幫忙檢查其中的內容是否匹配。所以,在創(chuàng )建Expression tree時(shí)需要額外小心,并且要注意檢查實(shí)際內容的類(lèi)型是否匹配。
一般來(lái)說(shuō)這并不是大問(wèn)題,因為Expression tree一般是由編譯器前端所生成的。在生成這棵樹(shù)之前,編譯器就應該負責完成類(lèi)型檢查等工作。
再談LINQ Expression tree的局限性LINQ的Expressiontree只能用于表示表達式,而且并不支持C#與VB.NET的所有表達式——與賦值相關(guān)的表達式都無(wú)法用Expressiontree表示。因此雖然有一元加和一元減這兩個(gè)一元運算符的對應物,卻沒(méi)有同為一元運算符的遞增(++)與遞減(--)的對應物。同時(shí),Expressiontree里面也沒(méi)有“變量”的概念,ParameterExpression只是用來(lái)表示參數,而參數的值在實(shí)際調用時(shí)綁定好之后就不能再改變。
但這些限制并沒(méi)有降低LINQ Expression tree理論上所能表示的計算邏輯的范圍。
1、引用透明性由于沒(méi)有“變量”的概念,通過(guò)Expression tree定義的表達式本身無(wú)法產(chǎn)生副作用,因而只要一棵Expression tree中不包含對外部的有副作用的方法的調用(Invoke/Call),其中的任何子樹(shù)重復多次產(chǎn)生的值都是一樣的。
這種性質(zhì)被稱(chēng)為
引用透明性(referential transparency)。舉例來(lái)說(shuō),假如原本有這樣的一組表達式:
- a = 1
- b = 2
- x = a + b
- y = a - b
- result = x * y - x % y
a = 1b = 2x = a + by = a - bresult = x * y - x % y
請把這些表達式中的符號看成“名字到值的綁定”而不是變量。如果有引用透明性,那么上面的表達式的求值就可以使用所謂“代替模型”(substitution model),可以展開(kāi)為以下形式而不改變原表達式計算出來(lái)的結果:
- result = (1 + 2) * (1 - 2) - (1 + 2) % (1 - 2)
result = (1 + 2) * (1 - 2) - (1 + 2) % (1 - 2)
同理,當我們實(shí)在需要在同一棵Expression tree中多次使用同一個(gè)子表達式的計算結果時(shí),只要多次使用同一棵子樹(shù)即可;既然沒(méi)有變量來(lái)暫時(shí)記住中間結果,那么就手動(dòng)把表達式展開(kāi)。用Expression tree的例子來(lái)說(shuō)明,那就是:
-
-
-
- var x = Expression.Parameter( typeof( int ), "x" );
- var y = Expression.Parameter( typeof( int ), "y" );
- var add = Expression.Add( x, y );
- var result = Expression.Lambda<Func<int, int, int>>(
- Expression.Multiply( add, add ),
- new ParameterExpression[ ] { x, y }
- );
- Console.WriteLine( result.Compile( )( 3, 4 ) );
// xPlusY = x + y// result = xPlusY * xPlusYvar x = Expression.Parameter( typeof( int ), "x" );var y = Expression.Parameter( typeof( int ), "y" );var add = Expression.Add( x, y );var result = Expression.Lambda<Func<int, int, int>>(Expression.Multiply( add, add ),new ParameterExpression[ ] { x, y });Console.WriteLine( result.Compile( )( 3, 4 ) ); // prints 49(前面已經(jīng)介紹過(guò)Expression tree的工廠(chǎng)方法,這里為了書(shū)寫(xiě)方便就不再顯式指明變量類(lèi)型了。)
能夠通過(guò)代替模型來(lái)求值是許多函數式語(yǔ)言的特征之一。求值順序不影響計算結果,一般有兩種求值順序:如果先求值再代替,稱(chēng)為應用序(applicative order),也叫做緊迫計算(eager evaluation)或者嚴格求值(strictevaluation);如果先替換在求值,則稱(chēng)為正則序(normal order),也叫做惰性求值(lazyevaluation)或者延遲求值(delayed evaluation)。
在LINQ Expression tree中,因為無(wú)法使用變量,也就難以方便的進(jìn)行嚴格求值;上面的例子手工展開(kāi)了表達式,實(shí)際上就是模擬了延遲求值的求值過(guò)程,在執行效率上會(huì )比嚴格求值要差一些。這雖然不影響計算的能力,但從性能角度看這么做是有負面影響的。
如果一棵Expressiontree中含有對外界的調用,上述的引用透明性就不成立了;我們無(wú)法確定一個(gè)調用是否含有副作用,所以無(wú)法保證不同的求值順序能得到相同的結果。而這個(gè)問(wèn)題需要特別的注意,因為.NET/CLR的類(lèi)型系統中沒(méi)任何有信息能表明一個(gè)函數是否有副作用,所以只能悲觀(guān)的假設包含對外界的調用的Expression tree不滿(mǎn)足引用透明性。
2、Lambda表達式LINQ Expressiontree允許定義與調用lambda表達式,甚至可以在表達式之中嵌套的定義lambda表達式。這奠定了Expressiontree理論上具有接近完備計算能力的基礎。所謂“完備”是指圖靈完備(Turing-complete)。無(wú)類(lèi)型的lambda演算是圖靈完備的,但在C#中lambda表達式需要指明類(lèi)型,而有些類(lèi)型通過(guò)C#的類(lèi)型系統難以表示,因此其表達力會(huì )比無(wú)類(lèi)型lambda演算要弱一些。
由于C#的類(lèi)型系統允許定義遞歸類(lèi)型,我們可以利用C#的lambda表達式來(lái)寫(xiě)遞歸的函數。同時(shí),方法體只是一個(gè)表達式(而不是語(yǔ)句塊)的lambda表達式可以用Expression tree表示,也就是說(shuō)我們可以用Expressiontree來(lái)寫(xiě)遞歸函數。有了遞歸,原本需要循環(huán)的邏輯都能用遞歸來(lái)編寫(xiě),也就突破了LINQ Expressiontree沒(méi)有循環(huán)結構的限制。下一篇就讓我們來(lái)看看實(shí)例,敬請期待 ^ ^