大大的回答正解了。
俺就来直接回答一下题主的那几个问题:
其中,当Body == Parameters[0] 时,使用新的ParameterExpression 。
我有三个问题:
1、两者相等意味着什么?为什么这样处理?
并不是Body == Parameters[0],而是Expression<TDelegate>.Body所指向的expression tree中任何节点与这个指定的Parameters[0]相同时,在visitor返回的新expression tree中将这个节点替换为新的ParameterExpression。
这个操作的意思是什么
@Ivony大大的回答已经说得很清楚了。
expr1与expr2的类型都是Expression<Func<T, bool>>,换句话说这个lambda的函数类型是接受一个类型为T的参数、返回值类型为bool的形式。所以在它们里面肯定只会有1个ParameterExpression,也就是由它们的Parameters[0]所引用的那个。
要提取出expr1与expr2的函数体,将它们组装出一个新的lambda,就必须要把它们的参数替换为新lambda的参数,否则无法建立起新lambda的参数与expr1/expr2已有的函数体之间的联系。
其实要组装expr1与expr2,除了提取出它们的函数体然后直接拼装之外,还可以选择直接调用它们然后组合结果,例如说:
using System; using System.Linq.Expressions; static class Program { static Expression<Func<T, bool>> AndAlso<T>( this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2) { 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<Func<T, bool>>(combine, param); } static void Main(string[] args) { Expression<Func<int, bool>> expr1 = a => a >= 0; Expression<Func<int, bool>> expr2 = b => b < 10; var combined = expr1.AndAlso(expr2); var func = combined.Compile(); Console.WriteLine("{0}, {1}", func(2), func(42)); // prints: // True, False } }
这个例子中,
expr1是
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
其中,a和b是
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也很费劲,,,,