C#高階程式設計六十五天----表示式樹

風靈使發表於2018-07-07

表示式樹

以前沒聽過表示式樹,只聽過表示式,而且聽過Lambda表示式,首先介紹一下.NET裡表示式樹的核心概念:講程式碼作為資料,他將一些程式碼表示為一個物件樹,樹中的每個節點本身都是一個表示式,不同的表示式型別代表能在程式碼中執行不同操作:二元操作,一元操作,方法呼叫等等.

System.Linq.Expression名稱空間包含了代表表示式的各個類.所有的表示式類都從Expression

類派生,Expresssion是個抽象類,主要包括的是一些靜態方法,這些方法用於生成其他表示式類的例項.Expression類還包含了兩個重要屬性:

  • Type屬性:代表了表示式求值結果的型別.比如,一個,一個表示式是要獲取一個字串的Length屬性,那麼該表示式的Type屬性應為int型別.

  • NodeType屬性:代表了表示式的種類.這個種類表示成Expression Type美劇的一個成員:LessThan,Invoke,Multiply,MemberAccess(好像有80多種).

案例:

Expression firstArg = Expression.Constant(2);

Expression secondArg = Expression.Constant(4);

Expression add = Expression.Add(firstArg, secondArg);

Console.WriteLine(add);

//輸出結果為 : <2 + 4>

Console.ReadKey();

分析:上述表示式會生成如下表示式:
這裡寫圖片描述

我突然這麼一說,你肯定覺得我在忽悠你,我騙人,像我這樣的正人君子不會騙你的.表示式中的”葉”表示式在程式碼中是最先建立的:表示式時自下而上構建的.這是由”表示式不易變”這一事實實現的.

將表示式樹編譯成委託

LambdaExpression是從Expression派生的型別.泛型類Expression<TDelegate>是從LamdaExpression派生的,其中反省引數TDelegate必須是委託型別.

LambdaExpression有個Comlile方法能建立恰當型別的一個委託.而Expression<TDelegate>Compile方法返回TDelegate型別的委託.案例如下:

Expression firstArg = Expression.Constant(2);

Expression secondArg = Expression.Constant(4);

Expression add = Expression.Add(firstArg, secondArg);

Expression<Func<int>> func = Expression.Lambda<Func<int>>(add);

Func<int> compiled = func.Compile();

Console.WriteLine(compiled);

Console.ReadKey();

分析:我們通過Expression.Lambda<TDelegate>(Expression expression)方法來建立Expression<TDelegate>型別物件,再呼叫其Complie方法獲取表示式樹編譯出的委託例項.

將C# Lambda表示式轉換成表示式樹

我們知道Lambda表示式能顯示或隱式的轉換成恰當的委託例項.但是,編譯器也能輕鬆的將Lambda表示式構建成一個表示式樹:

Lambda表示式轉換成表示式樹

Expression <Func<int>> return 5=()=>5;

但是,並不是所有的Lambda表示式都能轉換成表示式樹,有一些限制:不能將帶有一個語句塊的Lambda轉換成一個表示式樹—-只有對的那個表示式進行求值的Lambda才可以.表示式中不能包含賦值操作,因為表示式樹中表示不了這種操作.還有其他一些較少見的限制,總而言之,如果存在轉換問題,會在編譯時發現.

位於LINQ核心的表示式樹

表示式樹可以說是LINQ的核心之一,為什麼是LINQ的核心之一呢?因為表示式樹使得C#不再是僅僅能編譯成IL,我們可以通過C#生成一個表示式樹,將結果作為一箇中間格式,在將其轉換成目標平臺上的本機語言.比如SQL.我們常用的LINQ to SQL就是這樣生成的.

下圖展示了LINQ to ObjectsLINQ to SQL的不同路徑
這裡寫圖片描述

表示式樹的用途:

通過Expression的派生類中的各種節點型別,我們就可以構建表示式樹;然後可以把表示式樹轉換成相應的委託型別例項,最後執行委託例項的程式碼,但是我們不會繞這麼大的彎子來執行委託例項的程式碼.

表示式樹主要在LINQ to SQL中使用,我們需要將LINQ to SQL查詢表示式(返回Queryable型別)轉換成表示式樹.之所以需要轉換是因為LINQ to SQL查詢表示式不是在C#程式碼中執行的,LINQ to SQL查詢表示式被轉換成SQL,通過網路傳送,最後在資料庫伺服器中執行.

相關文章