C# 表達(dá)式樹Expression Trees的知識(shí)梳理
目錄
- 簡介
 - Lambda 表達(dá)式創(chuàng)建表達(dá)式樹
 - API 創(chuàng)建表達(dá)式樹
 - 解析表達(dá)式樹
 - 表達(dá)式樹的永久性
 - 編譯表達(dá)式樹
 - 執(zhí)行表達(dá)式樹
 - 修改表達(dá)式樹
 - 調(diào)試
 
簡介
表達(dá)式樹以樹形數(shù)據(jù)結(jié)構(gòu)表示代碼,其中每一個(gè)節(jié)點(diǎn)都是一種表達(dá)式,比如方法調(diào)用和 x < y 這樣的二元運(yùn)算等。
你可以對表達(dá)式樹中的代碼進(jìn)行編輯和運(yùn)算。這樣能夠動(dòng)態(tài)修改可執(zhí)行代碼、在不同數(shù)據(jù)庫中執(zhí)行 LINQ 查詢以及創(chuàng)建動(dòng)態(tài)查詢。
表達(dá)式樹還能用于動(dòng)態(tài)語言運(yùn)行時(shí) (DLR) 以提供動(dòng)態(tài)語言和 .NET Framework 之間的互操作性。
一、Lambda 表達(dá)式創(chuàng)建表達(dá)式樹
若 lambda 表達(dá)式被分配給 Expression<TDelegate> 類型的變量,則編譯器可以發(fā)射代碼以創(chuàng)建表示該 lambda 表達(dá)式的表達(dá)式樹。
C# 編譯器只能從表達(dá)式 lambda (或單行 lambda)生成表達(dá)式樹。
下列代碼示例使用關(guān)鍵字 Expression創(chuàng)建表示 lambda 表達(dá)式:
Expression<Action<int>> actionExpression = n => Console.WriteLine(n); Expression<Func<int, bool>> funcExpression1 = (n) => n < 0; Expression<Func<int, int, bool>> funcExpression2 = (n, m) => n - m == 0;
二、API 創(chuàng)建表達(dá)式樹
通過 API 創(chuàng)建表達(dá)式樹需要使用Expression 類
下列代碼示例展示如何通過 API 創(chuàng)建表示 lambda 表達(dá)式:num => num == 0
//通過 Expression 類創(chuàng)建表達(dá)式樹 // lambda:num => num == 0 ParameterExpression pExpression = Expression.Parameter(typeof(int)); //參數(shù):num ConstantExpression cExpression = Expression.Constant(0); //常量:0 BinaryExpression bExpression = Expression.MakeBinary(ExpressionType.Equal, pExpression, cExpression); //表達(dá)式:num == 0 Expression<Func<int, bool>> lambda = Expression.Lambda<Func<int, bool>>(bExpression, pExpression); //lambda 表達(dá)式:num => num == 0
代碼使用Expression 類的靜態(tài)方法進(jìn)行創(chuàng)建。
三、解析表達(dá)式樹
下列代碼示例展示如何分解表示 lambda 表達(dá)式 num => num == 0 的表達(dá)式樹。
Expression<Func<int, bool>> funcExpression = num => num == 0;
 //開始解析
 ParameterExpression pExpression = funcExpression.Parameters[0]; //lambda 表達(dá)式參數(shù)
 BinaryExpression body = (BinaryExpression)funcExpression.Body; //lambda 表達(dá)式主體:num == 0
 Console.WriteLine($"解析:{pExpression.Name} => {body.Left} {body.NodeType} {body.Right}");
四、表達(dá)式樹永久性
表達(dá)式樹應(yīng)具有永久性(類似字符串)。這意味著如果你想修改某個(gè)表達(dá)式樹,則必須復(fù)制該表達(dá)式樹然后替換其中的節(jié)點(diǎn)來創(chuàng)建一個(gè)新的表達(dá)式樹。 你可以使用表達(dá)式樹訪問者遍歷現(xiàn)有表達(dá)式樹。第七節(jié)介紹了如何修改表達(dá)式樹。
五、編譯表達(dá)式樹
Expression<TDelegate> 類型提供了 Compile 方法以將表達(dá)式樹表示的代碼編譯成可執(zhí)行委托。
//創(chuàng)建表達(dá)式樹
 Expression<Func<string, int>> funcExpression = msg => msg.Length;
 //表達(dá)式樹編譯成委托
 var lambda = funcExpression.Compile();
 //調(diào)用委托
 Console.WriteLine(lambda("Hello, World!"));
 //語法簡化
 Console.WriteLine(funcExpression.Compile()("Hello, World!"));
六、執(zhí)行表達(dá)式樹
執(zhí)行表達(dá)式樹可能會(huì)返回一個(gè)值,也可能僅執(zhí)行一個(gè)操作(例如調(diào)用方法)。
只能執(zhí)行表示 lambda 表達(dá)式的表達(dá)式樹。表示 lambda 表達(dá)式的表達(dá)式樹屬于 LambdaExpression 或 Expression<TDelegate> 類型。若要執(zhí)行這些表達(dá)式樹,需要調(diào)用 Compile 方法來創(chuàng)建一個(gè)可執(zhí)行委托,然后調(diào)用該委托。
const int n = 1;
 const int m = 2;
 //待執(zhí)行的表達(dá)式樹
 BinaryExpression bExpression = Expression.Add(Expression.Constant(n), Expression.Constant(m));
 //創(chuàng)建 lambda 表達(dá)式
 Expression<Func<int>> funcExpression = Expression.Lambda<Func<int>>(bExpression);
 //編譯 lambda 表達(dá)式
 Func<int> func = funcExpression.Compile();
 //執(zhí)行 lambda 表達(dá)式
 Console.WriteLine($"{n} + {m} = {func()}");
七、修改表達(dá)式樹
該類繼承 ExpressionVisitor 類,通過 Visit 方法間接調(diào)用 VisitBinary 方法將 != 替換成 ==?;惙椒?gòu)造類似于傳入的表達(dá)式樹的節(jié)點(diǎn),但這些節(jié)點(diǎn)將其子目錄樹替換為訪問器遞歸生成的表達(dá)式樹。
internal class Program
 {
 private static void Main(string[] args)
 {
 Expression<Func<int, bool>> funcExpression = num => num == 0;
 Console.WriteLine($"Source: {funcExpression}");
 var visitor = new NotEqualExpressionVisitor();
 var expression = visitor.Visit(funcExpression);
 Console.WriteLine($"Modify: {expression}");
 Console.Read();
 }
 /// <summary>
 /// 不等表達(dá)式樹訪問器
 /// </summary>
 public class NotEqualExpressionVisitor : ExpressionVisitor
 {
 public Expression Visit(BinaryExpression node)
 {
 return VisitBinary(node);
 }
 protected override Expression VisitBinary(BinaryExpression node)
 {
 return node.NodeType == ExpressionType.Equal
  ? Expression.MakeBinary(ExpressionType.NotEqual, node.Left, node.Right) //重新弄個(gè)表達(dá)式:用 != 代替 ==
  : base.VisitBinary(node);
 }
 }
 }
八、調(diào)試
8.1 參數(shù)表達(dá)式
ParameterExpression pExpression1 = Expression.Parameter(typeof(string)); ParameterExpression pExpression2 = Expression.Parameter(typeof(string), "msg");
圖8-1
圖8-2
從 DebugView 可知,如果參數(shù)沒有名稱,則會(huì)為其分配一個(gè)自動(dòng)生成的名稱。
const int num1 = 250; const float num2 = 250; ConstantExpression cExpression1 = Expression.Constant(num1); ConstantExpression cExpression2 = Expression.Constant(num2);
圖8-3
圖8-4
從 DebugView 可知,float 比 int 多了個(gè)后綴 F。
Expression lambda1 = Expression.Lambda<Func<int>>(Expression.Constant(250)); Expression lambda2 = Expression.Lambda<Func<int>>(Expression.Constant(250), "CustomName", null);
圖8-5
圖8-6
觀察 DebugView ,如果 lambda 表達(dá)式?jīng)]有名稱,則會(huì)為其分配一個(gè)自動(dòng)生成的名稱。
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時(shí)也希望多多支持我們!
上一篇:C#數(shù)組中List, Dictionary的相互轉(zhuǎn)換問題
欄 目:C#教程
本文標(biāo)題:C# 表達(dá)式樹Expression Trees的知識(shí)梳理
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/6049.html
您可能感興趣的文章
- 01-10C#編程自學(xué)之運(yùn)算符和表達(dá)式
 - 01-10C#正則表達(dá)式的6個(gè)簡單例子
 - 01-10C#中的正則表達(dá)式介紹
 - 01-10C#使用正則表達(dá)式實(shí)現(xiàn)首字母轉(zhuǎn)大寫的方法
 - 01-10輕松學(xué)習(xí)C#的正則表達(dá)式
 - 01-10詳解C#正則表達(dá)式Regex常用匹配
 - 01-10C#通過正則表達(dá)式實(shí)現(xiàn)提取網(wǎng)頁中的圖片
 - 01-10C#正則表達(dá)式Regex類的常用匹配
 - 01-10常用C#正則表達(dá)式匯總介紹
 - 01-10實(shí)例詳解C#正則表達(dá)式
 


閱讀排行
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
 - 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
 - 01-10WinForm限制窗體不能移到屏幕外的方法
 - 01-10WinForm繪制圓角的方法
 - 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
 - 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新
 - 01-10C#停止線程的方法
 - 01-10C#實(shí)現(xiàn)清空回收站的方法
 - 01-10C#通過重寫Panel改變邊框顏色與寬度的
 - 01-10C#實(shí)現(xiàn)讀取注冊表監(jiān)控當(dāng)前操作系統(tǒng)已
 
隨機(jī)閱讀
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
 - 01-10使用C語言求解撲克牌的順子及n個(gè)骰子
 - 01-11ajax實(shí)現(xiàn)頁面的局部加載
 - 08-05DEDE織夢data目錄下的sessions文件夾有什
 - 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
 - 04-02jquery與jsp,用jquery
 - 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
 - 08-05織夢dedecms什么時(shí)候用欄目交叉功能?
 - 01-10C#中split用法實(shí)例總結(jié)
 - 01-10delphi制作wav文件的方法
 


