为了证明这一点,我写了一个快速示例,其中通常会使用Quote
(请参阅标有感叹号的行),但我使用Constant
它并且工作得很好:
string[] array = { "one", "two", "three" };
// This example constructs an expression tree equivalent to the lambda:
// str => str.AsQueryable().Any(ch => ch == 'e')
Expression<Func<char, bool>> innerLambda = ch => ch == 'e';
var str = Expression.Parameter(typeof(string), "str");
var expr =
Expression.Lambda<Func<string, bool>>(
Expression.Call(typeof(Queryable), "Any", new Type[] { typeof(char) },
Expression.Call(typeof(Queryable), "AsQueryable",
new Type[] { typeof(char) }, str),
// !!!
Expression.Constant(innerLambda) // <--- !!!
),
str
);
// Works like a charm (prints one and three)
foreach (var str in array.AsQueryable().Where(expr))
Console.WriteLine(str);
我错过了什么?为什么是Expression.Quote()
和发明的特殊Quote
节点类型UnaryExpression
?
发布于 2018-04-23 16:51:15
引号运算符是一个操作符,它引发操作数的闭包语义。常量只是值。
引号和常量具有不同的含义,因此在表达式树中具有不同的表示形式。对两个截然不同的东西具有相同的表示方式是非常容易混淆的,并且容易出错
考虑以下:
(int s)=>(int t)=>s+t
现在,假设我们希望将其表示为表达式树,稍后将编译并执行它。表达树的主体应该是什么?
我们首先解释无趣的案例。如果我们希望它返回一个委托,那么是否使用Quote或Constant是个有争议的问题:
var ps = Expression.Parameter(typeof(int), "s");
var pt = Expression.Parameter(typeof(int), "t");
var ex1 = Expression.Lambda(
Expression.Lambda(
Expression.Add(ps, pt),
pt),
ps);
var f1a = (Func<int, Func<int, int>>) ex1.Compile();
var f1b = f1a(100);
Console.WriteLine(f1b(123));
困难的方式是说,而不是:
(int s)=>(int t)=>s+t
我们真正的意思是:
(int s)=>Expression.Lambda(Expression.Add(...
然后生成的表达式树认为:
Expression.Lambda(
Expression.Call(typeof(Expression).GetMethod("Lambda", ...
简单的方法是:
var ex2 = Expression.Lambda(
Expression.Quote(
Expression.Lambda(
Expression.Add(ps, pt),
pt)),
ps);
var f2a = (Func<int, Expression<Func<int, int>>>)ex2.Compile();
var f2b = f2a(200).Compile();
Console.WriteLine(f2b(123));
问题是:为什么不消除报价并使其做同样的事情?
var ex3 = Expression.Lambda(
Expression.Constant(
Expression.Lambda(
Expression.Add(ps, pt),
pt)),
ps);
var f3a = (Func<int, Expression<Func<int, int>>>)ex3.Compile();
var f3b = f3a(300).Compile();
Console.WriteLine(f3b(123));
发布于 2018-04-23 17:43:37
这个问题已经得到了很好的回答。我还想指出一个可以证明有关表达式树问题的资源:
微软有一个名为Dynamic Language Runtime的CodePlex项目。它的文档包括标题为“Expression Trees v2 Spec”的文档,它正是这样的:.NET 4中LINQ表达式树的规范。
例如,它说明了以下内容Expression.Quote
:
4.4.42报价 使用UnaryExpressions中的引号表示具有类型为Expression的“常量”值的表达式。与Constant节点不同,Quote节点特别处理包含的ParameterExpression节点。如果包含的ParameterExpression节点声明将在结果表达式中关闭的本地,则Quote将替换参考位置中的ParameterExpression。在计算Quote节点时的运行时,它会为ParameterExpression引用节点替换闭包变量引用,然后返回带引号的表达式。[...](第63-64页)
https://stackoverflow.com/questions/-100003302
复制相似问题