Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/amazon-s3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Expression.Quote()做什么?Expression.Constant()可以’;我还没有呢?_C#_Expression Trees - Fatal编程技术网

C# Expression.Quote()做什么?Expression.Constant()可以’;我还没有呢?

C# Expression.Quote()做什么?Expression.Constant()可以’;我还没有呢?,c#,expression-trees,C#,Expression Trees,注意:我知道前面的问题“,但如果你继续阅读,你会发现它没有回答我的问题 我理解Expression.Quote()的声明目的。但是,Expression.Constant()。因此,我不明白为什么需要Expression.Quote() 为了说明这一点,我编写了一个快速示例,其中习惯使用引号(请参见标有感叹号的行),但我使用了常量,它同样有效: string[] array = { "one", "two", "three" }; // This example constructs an e

注意:我知道前面的问题“,但如果你继续阅读,你会发现它没有回答我的问题

我理解
Expression.Quote()
的声明目的。但是,
Expression.Constant()。因此,我不明白为什么需要
Expression.Quote()

为了说明这一点,我编写了一个快速示例,其中习惯使用
引号
(请参见标有感叹号的行),但我使用了
常量
,它同样有效:

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);
string[]数组={“一”、“二”、“三”};
//此示例构造一个表达式树,该表达式树等效于lambda:
//str=>str.AsQueryable().Any(ch=>ch==“e”)
表达式innerLambda=ch=>ch='e';
var str=Expression.Parameter(typeof(string),“str”);
变量表达式=
Lambda(
Expression.Call(typeof(Queryable),“Any”,新类型[]{typeof(char)},
Expression.Call(typeof(Queryable),“AsQueryable”,
新类型[]{typeof(char)},str),
// !!!

Expression.Constant(innerLambda)//我认为这里的重点是树的表达能力。包含委托的常量表达式实际上只是包含一个碰巧是委托的对象。这比直接分解为一元和二元表达式的表达能力要差。

简短回答: quote运算符是一个在其操作数上诱导闭包语义的运算符。常量只是值

引号和常量具有不同的含义,因此在表达式树中具有不同的表示形式。对两个完全不同的事物具有相同的表示形式是非常容易混淆和出现错误的

长答覆: 考虑以下几点:

(int s)=>(int t)=>s+t
外部lambda是绑定到外部lambda参数的加法器的工厂

现在,假设我们希望将其表示为稍后将编译和执行的表达式树。表达式树的主体应该是什么?这取决于您希望编译状态返回委托还是表达式树

让我们先忽略这个无趣的案例。如果我们希望它返回一个委托,那么是否使用引号或常量的问题是一个没有实际意义的问题:

        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)=>Expression.Lambda(Expression.Add(...
然后为其生成表达式树,产生以下混乱:

废话废话,几十行反射代码来生成lambda。引号操作符的目的是告诉表达式树编译器,我们希望给定的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 ex2=Expression.Lambda(
表达式.引用(
Lambda(
表达式。添加(ps,pt),
pt),,
ps);
var f2a=(Func)ex2.Compile();
var f2b=f2a(200).Compile();
控制台写入线(f2b(123));
事实上,如果你编译并运行这段代码,你会得到正确的答案

请注意,quote运算符是在内部lambda上诱导闭包语义的运算符,内部lambda使用外部变量(外部lambda的形式参数)

问题是:为什么不取消报价,让它做同样的事情

        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));
var ex3=Expression.Lambda(
表达式.常数(
Lambda(
表达式。添加(ps,pt),
pt),,
ps);
var f3a=(Func)ex3.Compile();
var f3b=f3a(300).Compile();
控制台写入线(f3b(123));
常量不会产生闭包语义。为什么会这样?您说过这是一个常量。它只是一个值。交给编译器应该是完美的;编译器应该能够将该值转储到需要它的堆栈中

由于没有引起闭包,如果执行此操作,将在调用时出现“System.Int32未定义”类型的“变量”异常

(旁白:我刚刚回顾了从引用表达式树创建委托的代码生成器,不幸的是,我在2006年在代码中添加的一条注释仍然存在。仅供参考,当引用表达式树被运行时编译器具体化为委托时,提升的外部参数被快照为常量。这是有充分理由的。)为什么我这样写代码,我现在记不起来了,但它确实有一个令人讨厌的副作用,就是在外部参数的值上引入闭包,而不是在变量上引入闭包。显然,继承该代码的团队决定不修复该缺陷,所以如果您依赖于外部参数上的闭包的变异在编译的引用内部lambda中观察到,您会感到失望。然而,由于(1)变异形式参数和(2)都是非常糟糕的编程实践依靠外部变量的变异,我建议您将程序更改为不使用这两种糟糕的编程实践,而不是等待似乎不会出现的修复。对于错误,我深表歉意。)

所以,重复这个问题:

C#编译器可以将嵌套的lambda表达式编译成表达式树,其中包含expression.Constant()而不是expression.Quote(),以及任何希望将表达式树处理成其他查询语言(如SQL)的LINQ查询提供程序可以查找带有类型表达式的ConstantExpression,而不是带有特殊引号节点类型的UnaryExpression,其他所有内容都是相同的

你是科恩
        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));
Expression.Lambda(Expression.Add(ps, pt));
Expression.Lambda(
    Expression.Lambda(
              Expression.Add(ps, pt),
            pt), ps);
let f = Func<...>
return f; vs. return f(...);
Expression<Func<Func<int>>> f = () => () => 2;
Expression<Func<Expression<Func<int>>>> f = () => () => 2;