如何對 Expression 進行計算?
想解決一個EF6中 Expression 合併的問題,從 stackoverflow 搜到可用的結果 Combining two expressions (Expression&
源碼如此:&>)
public static Expression&
&> AndAlso& (
this Expression&&> expr1,
Expression&&> expr2)
{
var parameter = Expression.Parameter(typeof (T));var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);return Expression.Lambda&
&>(
Expression.AndAlso(left, right), parameter);
}private class ReplaceExpressionVisitor : ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}public override Expression Visit(Expression node)
其中,當Body == Parameters[0] 時,使用新的ParameterExpression 。
{
// 這裡意味著什麼?
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
我有三個問題:
1、兩者相等意味著什麼?為什麼這樣處理?2、如果我改寫成OrElse,這裡是不是有影響?3、如果想深入學習該方面的知識,有沒有推薦的書籍?
@Ivony 大大的回答正解了。俺就來直接回答一下題主的那幾個問題:
其中,當Body == Parameters[0] 時,使用新的ParameterExpression 。
我有三個問題:1、兩者相等意味著什麼?為什麼這樣處理?
並不是Body == Parameters[0],而是Expression&
這個操作的意思是什麼 @Ivony 大大的回答已經說得很清楚了。
expr1與expr2的類型都是Expression&
using System;
using System.Linq.Expressions;
static class Program {
static Expression&
this Expression&
Expression&
var param = Expression.Parameter(typeof(T));
var invoke1 = Expression.Invoke(expr1, param);
var invoke2 = Expression.Invoke(expr2, param);
var combine = Expression.AndAlso(invoke1, invoke2);
return Expression.Lambda&
}
static void Main(string[] args) { 這個例子中,
Expression&
Expression&
a =&> a &>= 0
expr2 是
b =&> b &< 10
而組合出來的combined的邏輯是:
x =&> expr1(x) expr2(x)
也就是直接調用expr1與expr2得到結果來做操作得到結果(注意這裡有短路求值語義喔,雖然這個例子中沒有副作用看不出來短路與否的區別)。
而"Invoke",函數調用,用lambda演算的角度來看,就是β-reduction——function application。expr1(x) ,換個寫法就是 (a =&> a &>= 0)(x) ,經過β-reduction之後得到的就是 x &>= 0。對 expr2(x) 如法炮製,(b =&> b &< 10)(x) 應用β-reduction得到 x &< 10。
把這倆放在combined中,就變成了:x =&> (x &>= 0) (x &< 10)
可以看到,正如 @Ivony 大大的回答所說,這就是「把函數內聯」的效果。
題主所給出的示例代碼中,把expr1與expr2的函數體提取出來,並將它們的參數替換為combined的參數,這就是β-reduction。
2、如果我改寫成OrElse,這裡是不是有影響?
如果您需要的組合方式是 expr1(x) || expr2(x) 的話,那麼OrElse()就對了
3、如果想深入學習該方面的知識,有沒有推薦的書籍?
這個問題好難回答…其實都是一些編譯原理的基本原理的應用,混入一些lambda演算的皮毛。
LINQ Expression Tree / DLR Expression Tree就是一種比較典型的樹形/DAG的IR,對它的各種操作都是編譯器中端會涉及的知識,所以如果有一些編譯原理基礎的話學習起來就會很輕鬆。不過沒有基礎也沒關係,就是棵表達程序語義的樹/DAG而已…我以前寫過一些博客文章跟LINQ與DLR Expression Tree相關,如果有興趣也可以參考一下:RednaxelaFX的博客 - DLR分類文章列表 - ITeye技術網站
有本書介紹DLR(與LINQ Expression Tree)的特性和用法的,或許可以參考:Pro DLR in .NET 4
簡單說就是這樣,我們假定有兩個函數:
a =&> a &< 10
b =&> b &> 0
expr1.Parameters[0]
和
expr2.Parameters[0]
a &< 10
b &> 0
則是
expr1.Body
和
expr2.Body
x =&> //x 就是 parameter = Expression.Parameter(typeof (T));
x &< 10 //第一個函數,用x替換了a
//AndAlso
x &> 0 //第二個函數,用x替換了b
就是這個樣子:
x =&> x &< 10 x &> 0
所以簡單說,就是把函數內聯。
書籍我自己也想找人給我推薦,我啃DLR也很費勁,,,,這就是看msdn能看懂的東西 沒必要看書啦。。。
原理前面幾位大神都已經說的很清楚了 書籍的話我推薦c# in deepth 裡面有講到lambada 怎麼玩兒另外裡面用到了設計模式裡面一個叫做訪問者模式的東西 expressionvisiter會替你去遍歷表達式(表達式本質上是一顆二叉樹) 在遍歷的時候你的代碼等於說是把expr1和expr2表達式的參數統一了起來 (Var parameter)這樣兩者才可以andalso 不然他們兩個因為是兩個不同的實例所以其實是沒法放在一起的建議可以搜索訪問者模式以及expression Visitor這個類的用法。。。。最後還有c# in deepth這本書最後表示linq lambda真的很好玩兒 可以寫各種有趣的東西。。。
推薦閱讀:
※在 ASP.NET Core 已經推出的今天,IIS 會被砍嗎?
※老師說linq語句過時了,是真的嗎?
※Entity Framework裡面 使用Code First 還是 Model First / Database First?
※如何在C#中存儲大量數據而不引發OutOfMemoryException?
※為什麼聽說過 JVM 調優,卻沒聽說過 CLR 調優?