拼接Expression表示式樹
什麼是表示式樹
表示式樹以樹形資料結構表示程式碼,其中每一個節點都是一種表示式,它將我們原來可以直接由程式碼編寫的邏輯以表示式的方式儲存在樹狀的結構裡,從而可以在執行時去解析這個樹,然後執行,實現動態的編輯和執行程式碼。在.Net 裡面的Linq to SQL就是對錶達式樹的解析。
表示式和表示式樹,表示式相信大家都知道,比如x+5或者5,都可以算是表示式,而表示式樹裡面的樹指的二叉樹,也就是表示式的集合,C#中的Expression類就是表示式類。對於一棵表示式樹,其葉子節點都是引數或者常數,非葉子節點都是運算子或者控制符。
表示式樹主要由下面四部分組成:
1、Body 主體部分
2、Parameters 引數部分
3、NodeType 節點型別
4、Lambda表示式型別
初始化表示式樹和初始化委託 #### 初始化委託的時候,實際上就是定義了一個匿名方法,相當於在做事
如:
Func<string> func = () => "hello";
而反編譯後可以看到,實際上系統也是定義了匿名方法
Func<string> func = delegate{return "hello"; };
初始化表示式的時候,他是一個表示式樹,相當於在描述事物
Expression<Func<string>> func_expression = ()=>"hello world";
反編譯後可以看到系統會幫我們拼接表示式
Expression<Func<string>> func_expression =
Expression.Lambda<Func<string>>(Expression.Constant("hello world", typeof(string)), Array.Empty<ParameterExpression>());
手動拼接表示式樹
系統拼接表示式也是從左邊到右邊依次拼接,以先算括號,然後再乘除和方法
表示式樹常用物件:
ParameterExpression:引數表示式,表示變數 ConstantExpression:常量表達式,常量 MemberExpression:成員表示式,類裡的成員(屬性、欄位等) MemberBinding:成員繫結 BinaryExpression:二元表示式 二元表示式BinaryExpression的常用節點型別(NodeType): Call:表示方法 Add:+ Multiply:* Subtract:- Divide:/ Modulo:% Expression裡的常用方法 : Compile():編譯表示式
藉助linqpad圖示瞭解拼接過程
1,Expression<Func<int,int>> expression = (a) => a*2;
步驟1:
步驟2:
2,Expression<Func<int,int>> func_expression = (a,b) =>(a+b)*Convert.ToInt32(a)+b;
步驟1:
步驟2:
步驟3:
步驟4:
步驟5:
瞭解拼接過程我們就可以根據Expression的節點型別拼接常見的樹了
1
Expression<Func<int>> expression = () => 10;
用反編譯後我們發現和反編譯後系統幫我們拼接的表示式基本一致的結果
//用反編譯可以看到系統幫我們生成的表示式
Expression<Func<int>> expression2 =
Expression.Lambda<Func<int>>(Expression.Constant(10, typeof(int)), Array.Empty<ParameterExpression>());
//自己建立這個表示式
//1、建立常量表達式
ConstantExpression constant = Expression.Constant(10, typeof(int));
//2、生成表示式
Expression<Func<int>> myexpression = Expression.Lambda<Func<int>>(constant, null);//因為沒有引數,所以直接給null即可
//編譯後呼叫
Console.WriteLine(myexpression.Compile().Invoke());
2
Expression<Func<int, int>> expression = (a) => 10 + a;
//用反編譯檢視系統幫我們生成的expression表示式樹如下:
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "a");
Expression<Func<int, int>> expression =
Expression.Lambda<Func<int, int>>(Expression.Add(Expression.Constant(10, typeof(int)), parameterExpression), new ParameterExpression[1] { parameterExpression });
//自己拼接
//1、建立常量表達式
ConstantExpression constant = Expression.Constant(10);//左邊left
//2、建立引數列表表示式,第一個引數,該引數列表的資料型別,第二個引數,引數名
ParameterExpression parameterExp = Expression.Parameter(typeof(int), "a");//右邊right
//3、建立二元表示式(相當於結果body,這個裡面就包括了left和right兩個表示式),add方法需要兩個表示式左邊和右邊的兩個表示式
BinaryExpression binaryExp = Expression.Add(constant, parameterExp);//Body
//4、生成表示式,Lambda方法裡需要給它泛型具體型別,然後引數裡第一個引數是body,第二個是引數列表
Expression<Func<int, int>> myExpression = Expression.Lambda<Func<int, int>>(binaryExp, new ParameterExpression[] { parameterExp });
//編譯後呼叫該表示式樹為我們生成的委託
Console.WriteLine(myExpression.Compile().Invoke(5));
3
Expression<Func<int, int, int>> expression = (y, x) => 5 + y * y - x;
////系統幫我們拼接的
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "y");
ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "x");
Expression<Func<int, int, int>> expression =
Expression.Lambda<Func<int, int, int>>(
Expression.Subtract(Expression.Add(Expression.Constant(5, typeof(int)), Expression.Multiply(parameterExpression, parameterExpression)), parameterExpression2),
new ParameterExpression[2] { parameterExpression, parameterExpression2 }
);
//1、寫最裡層的兩個引數表示式,因為這一步先算兩個引數相乘,但是兩個引數名一樣,所以這裡定義一個引數表示式即可
ParameterExpression parameterExpY = Expression.Parameter(typeof(int), "y");//右邊right和左邊left共用y
//2、建立他們兩個引數的二元表示式
BinaryExpression binaryExpMultiply = Expression.Multiply(parameterExpY, parameterExpY);//right : y * y
//3、開始拼接第2層,建立一個常量表達式5,第一個引數,值,第二個引數,型別
ConstantExpression constant = Expression.Constant(5, typeof(int));//left : 5
//4、建立一個二元表示式+,左邊是常量,右邊是上個二元表示式的結果
BinaryExpression binaryExpAdd = Expression.Add(constant, binaryExpMultiply);//left : 5 + y * y
//5、開始拼接第3層,建立一個引數表示式
ParameterExpression parameterExpX = Expression.Parameter(typeof(int), "x");//right
//5、建立二元表示式-
BinaryExpression binaryExpSubtract = Expression.Subtract(binaryExpAdd, parameterExpX);//5 + y * y - x
//6、最後將body和lambda組成一個表示式
Expression<Func<int, int, int>> expression = Expression.Lambda<Func<int, int, int>>(binaryExpSubtract, new ParameterExpression[] { parameterExpY, parameterExpX });
//編譯後呼叫
Console.WriteLine(expression.Compile().Invoke(5, 6));
4
``