C# 表達(dá)式樹(shù)Expression Trees的知識(shí)梳理
目錄
- 簡(jiǎn)介
- Lambda 表達(dá)式創(chuàng)建表達(dá)式樹(shù)
- API 創(chuàng)建表達(dá)式樹(shù)
- 解析表達(dá)式樹(shù)
- 表達(dá)式樹(shù)的永久性
- 編譯表達(dá)式樹(shù)
- 執(zhí)行表達(dá)式樹(shù)
- 修改表達(dá)式樹(shù)
- 調(diào)試
簡(jiǎn)介
表達(dá)式樹(shù)以樹(shù)形數(shù)據(jù)結(jié)構(gòu)表示代碼,其中每一個(gè)節(jié)點(diǎn)都是一種表達(dá)式,比如方法調(diào)用和 x < y 這樣的二元運(yùn)算等。
你可以對(duì)表達(dá)式樹(shù)中的代碼進(jìn)行編輯和運(yùn)算。這樣能夠動(dòng)態(tài)修改可執(zhí)行代碼、在不同數(shù)據(jù)庫(kù)中執(zhí)行 LINQ 查詢(xún)以及創(chuàng)建動(dòng)態(tài)查詢(xún)。
表達(dá)式樹(shù)還能用于動(dòng)態(tài)語(yǔ)言運(yùn)行時(shí) (DLR) 以提供動(dòng)態(tài)語(yǔ)言和 .NET Framework 之間的互操作性。
一、Lambda 表達(dá)式創(chuàng)建表達(dá)式樹(shù)
若 lambda 表達(dá)式被分配給 Expression<TDelegate> 類(lèi)型的變量,則編譯器可以發(fā)射代碼以創(chuàng)建表示該 lambda 表達(dá)式的表達(dá)式樹(shù)。
C# 編譯器只能從表達(dá)式 lambda (或單行 lambda)生成表達(dá)式樹(shù)。
下列代碼示例使用關(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á)式樹(shù)
通過(guò) API 創(chuàng)建表達(dá)式樹(shù)需要使用Expression 類(lèi)
下列代碼示例展示如何通過(guò) API 創(chuàng)建表示 lambda 表達(dá)式:num => num == 0
//通過(guò) Expression 類(lèi)創(chuàng)建表達(dá)式樹(shù) // 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 類(lèi)的靜態(tài)方法進(jìn)行創(chuàng)建。
三、解析表達(dá)式樹(shù)
下列代碼示例展示如何分解表示 lambda 表達(dá)式 num => num == 0 的表達(dá)式樹(shù)。
Expression<Func<int, bool>> funcExpression = num => num == 0; //開(kāi)始解析 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á)式樹(shù)永久性
表達(dá)式樹(shù)應(yīng)具有永久性(類(lèi)似字符串)。這意味著如果你想修改某個(gè)表達(dá)式樹(shù),則必須復(fù)制該表達(dá)式樹(shù)然后替換其中的節(jié)點(diǎn)來(lái)創(chuàng)建一個(gè)新的表達(dá)式樹(shù)。 你可以使用表達(dá)式樹(shù)訪問(wèn)者遍歷現(xiàn)有表達(dá)式樹(shù)。第七節(jié)介紹了如何修改表達(dá)式樹(shù)。
五、編譯表達(dá)式樹(shù)
Expression<TDelegate> 類(lèi)型提供了 Compile 方法以將表達(dá)式樹(shù)表示的代碼編譯成可執(zhí)行委托。
//創(chuàng)建表達(dá)式樹(shù) Expression<Func<string, int>> funcExpression = msg => msg.Length; //表達(dá)式樹(shù)編譯成委托 var lambda = funcExpression.Compile(); //調(diào)用委托 Console.WriteLine(lambda("Hello, World!")); //語(yǔ)法簡(jiǎn)化 Console.WriteLine(funcExpression.Compile()("Hello, World!"));
六、執(zhí)行表達(dá)式樹(shù)
執(zhí)行表達(dá)式樹(shù)可能會(huì)返回一個(gè)值,也可能僅執(zhí)行一個(gè)操作(例如調(diào)用方法)。
只能執(zhí)行表示 lambda 表達(dá)式的表達(dá)式樹(shù)。表示 lambda 表達(dá)式的表達(dá)式樹(shù)屬于 LambdaExpression 或 Expression<TDelegate> 類(lèi)型。若要執(zhí)行這些表達(dá)式樹(shù),需要調(diào)用 Compile 方法來(lái)創(chuàng)建一個(gè)可執(zhí)行委托,然后調(diào)用該委托。
const int n = 1; const int m = 2; //待執(zhí)行的表達(dá)式樹(shù) 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á)式樹(shù)
該類(lèi)繼承 ExpressionVisitor 類(lèi),通過(guò) Visit 方法間接調(diào)用 VisitBinary 方法將 != 替換成 ==。基類(lèi)方法構(gòu)造類(lèi)似于傳入的表達(dá)式樹(shù)的節(jié)點(diǎn),但這些節(jié)點(diǎn)將其子目錄樹(shù)替換為訪問(wèn)器遞歸生成的表達(dá)式樹(shù)。
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á)式樹(shù)訪問(wèn)器 /// </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ù)沒(méi)有名稱(chēng),則會(huì)為其分配一個(gè)自動(dòng)生成的名稱(chē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)]有名稱(chēng),則會(huì)為其分配一個(gè)自動(dòng)生成的名稱(chēng)。
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
- C#之Expression表達(dá)式樹(shù)實(shí)例
- C#表達(dá)式樹(shù)Expression動(dòng)態(tài)創(chuàng)建表達(dá)式
- 淺談c#表達(dá)式樹(shù)Expression簡(jiǎn)單類(lèi)型比較demo
- C#表達(dá)式樹(shù)Expression基礎(chǔ)講解
- C#表達(dá)式樹(shù)的基本用法講解
- C#表達(dá)式樹(shù)講解
- C#表達(dá)式樹(shù)基礎(chǔ)教程
- C#執(zhí)行表達(dá)式樹(shù)(Expression Tree)的具體使用
- C#表達(dá)式樹(shù)(Expression Trees)的使用
相關(guān)文章
c#菜單動(dòng)態(tài)合并的實(shí)現(xiàn)方法
這篇文章主要介紹了c#菜單動(dòng)態(tài)合并的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10C#中關(guān)于double.ToString()的用法
這篇文章主要介紹了C#中關(guān)于double.ToString()的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02DataTable的AcceptChanges()和RejectChanges()方法介紹并實(shí)現(xiàn)DataGridView
這篇文章介紹了DataTable的AcceptChanges()和RejectChanges()方法并實(shí)現(xiàn)DataGridView數(shù)據(jù)增、刪、改,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02在Winform動(dòng)態(tài)啟動(dòng)、控制臺(tái)命令行的方法
winForm 程序輸出類(lèi)型為 windows 程序(不是命令行程序)在運(yùn)行時(shí)想輸入一些信息編譯開(kāi)發(fā)調(diào)試,如何實(shí)現(xiàn)這一功能2013-02-02簡(jiǎn)單聊聊C#字符串構(gòu)建利器StringBuilder
因?yàn)镾tring類(lèi)型代表不可變字符串,所以無(wú)法對(duì)當(dāng)前String類(lèi)型實(shí)例進(jìn)行處理.所以FCL提供了System.Text.StringBuilder類(lèi)型,下面這篇文章主要給大家介紹了關(guān)于C#字符串構(gòu)建利器StringBuilder的相關(guān)資料,需要的朋友可以參考下2022-03-03WPF實(shí)現(xiàn)授權(quán)碼顯示密文并支持換行
這篇文章主要為大家詳細(xì)介紹了如何使用WPF實(shí)現(xiàn)授權(quán)碼顯示密文并支持換行,文中的示例代碼講解詳細(xì),有需要的小伙伴可以參考一下2024-10-10