首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Expression.Quote()做了什么,Expression.Constant()不能做到这一点?

Expression.Quote()做了什么,Expression.Constant()不能做到这一点?
EN

Stack Overflow用户
提问于 2018-04-23 08:34:50
回答 2查看 0关注 0票数 0

为了证明这一点,我写了一个快速示例,其中通常会使用Quote(请参阅标有感叹号的行),但我使用Constant它并且工作得很好:

代码语言:javascript
复制
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

EN

回答 2

Stack Overflow用户

发布于 2018-04-23 16:51:15

简短的回答:

引号运算符是一个操作符,它引发操作数的闭包语义。常量只是值。

引号和常量具有不同的含义,因此在表达式树中具有不同的表示形式。对两个截然不同的东西具有相同的表示方式是非常容易混淆的,并且容易出错

考虑以下:

代码语言:javascript
复制
(int s)=>(int t)=>s+t

现在,假设我们希望将其表示为表达式树,稍后将编译并执行它。表达树的主体应该是什么?

我们首先解释无趣的案例。如果我们希望它返回一个委托,那么是否使用Quote或Constant是个有争议的问题:

代码语言:javascript
复制
        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));

困难的方式是说,而不是:

代码语言:javascript
复制
(int s)=>(int t)=>s+t

我们真正的意思是:

代码语言:javascript
复制
(int s)=>Expression.Lambda(Expression.Add(...

然后生成的表达式树认为:

代码语言:javascript
复制
        Expression.Lambda(
            Expression.Call(typeof(Expression).GetMethod("Lambda", ...

简单的方法是:

代码语言:javascript
复制
        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));

问题是:为什么不消除报价并使其做同样的事情?

代码语言:javascript
复制
        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));
票数 0
EN

Stack Overflow用户

发布于 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页)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/-100003302

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档