什么防爆pression.Quote()做防爆pression.Constant()已经不能做什么? [英] What does Expression.Quote() do that Expression.Constant() can’t already do?

查看:142
本文介绍了什么防爆pression.Quote()做防爆pression.Constant()已经不能做什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:我知道前面的问题什么是LINQ的防爆pression.Quote方法的目的是什么? ,但如果你在你读会看到,它并没有回答我的问题。

Note: I am aware of the earlier question "What is the purpose of LINQ's Expression.Quote method?", but if you read on you will see that it doesn’t answer my question.

我明白了什么的既定目的防爆pression.Quote()是。然而,防爆pression.Constant()可用于同一目的(除了所有施行前pression .Constant()已用于)。因此,我不明白为什么防爆pression.Quote()在所有需要。

I understand what the stated purpose of Expression.Quote() is. However, Expression.Constant() can be used for the same purpose (in addition to all the purposes that Expression.Constant() is already used for). Therefore, I don’t understand why Expression.Quote() is at all required.

要证明这一点,我写了一个简单的例子,其中一个会习惯使用报价(见标有惊叹号线),但我用,而不是和它的工作同样出色:

To demonstrate this, I have written a quick example where one would customarily use Quote (see the line marked with exclamation points), but I used Constant instead and it worked equally well:

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);

的输出expr.ToString()是上是相同的,太(我是使用报价)。

The output of expr.ToString() is the same for both, too (whether I use Constant or Quote).

鉴于上述意见,看来防爆pression.Quote()是多余的。可能已经取得的C#编译器编译嵌套的lambda前pressions涉及到一名前pression树防爆pression.Constant()而不是防爆pression.Quote(),而要处理的前pression树到其他查询语言(如SQL)的LINQ查询提供商可以看出来为 ConstantEx pression 类型防爆pression&LT; TDelegate&GT; ,而不是 UnaryEx pression 报价节点类型,以及其他一切>将是一样的。

Given the above observations, it appears that Expression.Quote() is redundant. The C# compiler could have been made to compile nested lambda expressions into an expression tree involving Expression.Constant() instead of Expression.Quote(), and any LINQ query provider that wants to process expression trees into some other query language (such as SQL) could look out for a ConstantExpression with type Expression<TDelegate> instead of a UnaryExpression with the special Quote node type, and everything else would be the same.

我在想什么?为什么防爆pression.Quote()和特殊报价节点类型 UnaryEx pression 发明的?

What am I missing? Why was Expression.Quote() and the special Quote node type for UnaryExpression invented?

推荐答案

该帖运算符是一个的运营商的其中的诱导其操作数封语义的。常量只是值。

Short answer:

The quote operator is an operator which induces closure semantics on its operand. Constants are just values.

行情和常量有不同的含义的,因此具有的不同再$ P $在离pression树psentations 的。具有两种不同的事物一样重presentation是的非常的混乱和错误倾向。

Quotes and constants have different meanings and therefore have different representations in an expression tree. Having the same representation for two very different things is extremely confusing and bug prone.

考虑以下几点:

(int s)=>(int t)=>s+t

外拉姆达是一个工厂绑定到外部拉姆达的参数法器。

The outer lambda is a factory for adders that are bound to the outer lambda's parameter.

现在,假设我们希望重新present这是一个前pression树将在稍后进行编译和执行。我应该恩pression树的身体呢?的这取决于你是否希望编译状态返回委托或前pression树。

Now, suppose we wish to represent this as an expression tree that will later be compiled and executed. What should the body of the expression tree be? It depends on whether you want the compiled state to return a delegate or an expression tree.

让我们开始驳回无趣的情况。如果我们希望它返回一个代表那么是否使用引用的问题或常量是一个有争议的问题:

Let's begin by dismissing the uninteresting case. If we wish it to return a delegate then the question of whether to use Quote or Constant is a moot point:

        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));

该拉姆达有一个嵌套的lambda;编译器生成的内部拉姆达作为代表到功能关闭了对外部拉姆达产生的功能的状态。我们不需要更多考虑这种情况。

The lambda has a nested lambda; the compiler generates the interior lambda as a delegate to a function closed over the state of the function generated for the outer lambda. We need consider this case no more.

假设我们希望编译状态返回的前pression树的内部的。有两种方法可以做到这一点:最简单的方式和硬盘的方式。

Suppose we wish the compiled state to return an expression tree of the interior. There are two ways to do that: the easy way and the hard way.

难的方法是说,而不是

(int s)=>(int t)=>s+t

我们真正的意思是

what we really mean is

(int s)=>Expression.Lambda(Expression.Add(...

然后生成的的前任pression树,制作的这个烂摊子的:

And then generate the expression tree for that, producing this mess:

        Expression.Lambda(
            Expression.Call(typeof(Expression).GetMethod("Lambda", ...

等等等等,几十个反射code的线条,使拉姆达。的报价运营商的目的是要告诉恩pression树编译器,我们希望给定的lambda来被视为前pression树,而不是作为一个功能,而无需显式生成恩pression树生成code

简单的方法是:

        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));

事实上,如果你编译并运行这个code你得到正确的答案。

And indeed, if you compile and run this code you get the right answer.

注意引号操作符是诱导其上使用一个外部变量,外拉姆达的形式参数的内部拉姆达关闭语义操作。

Notice that the quote operator is the operator which induces closure semantics on the interior lambda which uses an outer variable, a formal parameter of the outer lambda.

现在的问题是:为什么不能消除报价,使这个做同样的事情。

The question is: why not eliminate Quote and make this do the same thing?

        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));

常数不诱导封闭语义。何必呢?你说,这是一起的的。这只是一个值。它应该是完美的,因为交给编译器;编译器应该能够只生成值转储到需要的地方堆栈。

The constant does not induce closure semantics. Why should it? You said that this was a constant. It's just a value. It should be perfect as handed to the compiler; the compiler should be able to just generate a dump of that value to the stack where it is needed.

由于没有关闭所致,如果你这样做,你会得到一个变量的'类型'System.Int32'是没有定义异常的调用。

Since there is no closure induced, if you do this you'll get a "variable 's' of type 'System.Int32' is not defined" exception on the invocation.

(题外话:我刚刚回顾了委托创作code发电机转引自前pression树木,不幸的是,我投入了code早在2006年评论仍然存在仅供参考。 ,在悬挂外参数的当引述前pression树被物化为运行时编译器的委托快照的成恒定的。有就是为什么我写了code一个很好的理由,这一点我不是在这个确切的时刻记得,但它确实有过的的外部参数,而不是封了的变量的。显然,球队引入封闭的讨厌的副作用的方法它继承了code决定不修复该漏洞,所以如果你在突变依靠封闭在一个编译引用内部拉姆达被观察,你会感到失望。不过,既然是外参数一个pretty不好的编程习惯既(1)变异形式参数和(2)依靠外变量突变,我会建议你改变你的计划,不要使用这两种坏的编程习惯,而不是等待这似乎没有修复到即将到来。道歉的错误。)

(Aside: I've just reviewed the code generator for delegate creation from quoted expression trees, and unfortunately a comment that I put into the code back in 2006 is still there. FYI, the hoisted outer parameter is snapshotted into a constant when the quoted expression tree is reified as a delegate by the runtime compiler. There was a good reason why I wrote the code that way which I do not recall at this exact moment, but it does have the nasty side effect of introducing closure over values of outer parameters rather than closure over variables. Apparently the team which inherited that code decided to not fix that flaw, so if you are relying upon mutation of a closed-over outer parameter being observed in a compiled quoted interior lambda, you're going to be disappointed. However, since it is a pretty bad programming practice to both (1) mutate a formal parameter and (2) rely upon mutation of an outer variable, I would recommend that you change your program to not use these two bad programming practices, rather than waiting for a fix which does not appear to be forthcoming. Apologies for the error.)

因此​​,重复问题:

可能已经取得的C#编译器编译嵌套的lambda前pressions到涉及防爆pression.Constant(一个前pression树),而不是前pression.Quote()和即要处理的前pression树木成一些其他的查询语言(如SQL)的LINQ查询提供者也可以看出来的型防爆pression而不是UnaryEx pression与ConstantEx pression特殊报价节点类型,以及其他一切将是相同的。

The C# compiler could have been made to compile nested lambda expressions into an expression tree involving Expression.Constant() instead of Expression.Quote(), and any LINQ query provider that wants to process expression trees into some other query language (such as SQL) could look out for a ConstantExpression with type Expression instead of a UnaryExpression with the special Quote node type, and everything else would be the same.

您是正确的。我们的可能的EN code,意思是诱导封闭语义这个值通过的使用常量前pression类型作为标志的语义信息。

You are correct. We could encode semantic information that means "induce closure semantics on this value" by using the type of the constant expression as a flag.

恒,那么将有意思是用这个恒定值,除非的类型恰好是一个前pression树类型的的值是一个有效前pression树,在这种情况下,代替使用从重写定前pression树的内部以诱导封闭语义任何外lambda表达式的上下文中所产生的前pression树的值我们可能在现在。

"Constant" would then have the meaning "use this constant value, unless the type happens to be an expression tree type and the value is a valid expression tree, in which case, instead use the value that is the expression tree resulting from rewriting the interior of the given expression tree to induce closure semantics in the context of any outer lambdas that we might be in right now.

但为什么的将会的,我们做的傻事? 的报价运算符是一个疯狂的复杂运算符的,它应该使用的明确的,如果你要使用它。你这表明为了俭省约不增加一个额外的工厂方法和节点类型跻身几十个已经存在,我们一个奇怪的角落情况下增加常量,使常数有时逻辑常量,有时他们被重写lambda表达式与封闭的语义。

But why would we do that crazy thing? The quote operator is an insanely complicated operator, and it should be used explicitly if you're going to use it. You're suggesting that in order to be parsimonious about not adding one extra factory method and node type amongst the several dozen already there, that we add a bizarre corner case to constants, so that constants are sometimes logically constants, and sometimes they are rewritten lambdas with closure semantics.

这本来也有几分奇怪效果不变并不意味着使用此值。假设你的希望的上述第三种情况来编译前pression树成手了有一个不重写参考外的前pression树委托一些奇怪的原因变量?为什么?也许是因为的您正在测试你的编译器的和只想通过对不断通过这样你可以稍后再执行一些其他的分析。你的建议会作出这样的不可能的;出现这种情况是前pression树类型的任何常数会被改写无关。其中有一个合理的预期,不变是指使用此值。 恒是做什么,我说节点。恒处理器的工作不是在你的猜出意思的基于类型说。

It would also have the somewhat odd effect that constant doesn't mean "use this value". Suppose for some bizarre reason you wanted the third case above to compile an expression tree into a delegate that hands out an expression tree that has a not-rewritten reference to an outer variable? Why? Perhaps because you are testing your compiler and want to just pass the constant on through so that you can perform some other analysis on it later. Your proposal would make that impossible; any constant that happens to be of expression tree type would be rewritten regardless. One has a reasonable expectation that "constant" means "use this value". "Constant" is a "do what I say" node. The constant processor's job is not to guess at what you meant to say based on the type.

和记当然,你现在把认识的负担(即理解是常数复杂,在一个案例中的意思是不变,并根据一个标志,是的诱导封闭语义的语义类型系统的)后的每个的提供者,做一个前pression树的语义分析,而不仅仅是在微软供应商。 如何与第三方供应商的许多人都弄错了吗?

And note of course that you are now putting the burden of understanding (that is, understanding that constant has complicated semantics that mean "constant" in one case and "induce closure semantics" based on a flag that is in the type system) upon every provider that does semantic analysis of an expression tree, not just upon Microsoft providers. How many of those third-party providers would get it wrong?

报价在挥舞大红旗,上面写着嘿,伙计,看过来,我是一个嵌套的lambda前pression我有古怪的语义,如果我关闭了外变量!而恒是在说:我没有什么比一个更值;用我,你认为合适。当某物是复杂和危险的,我们希望被使它波红旗,不通过使用户通过型系统挖隐藏该事实,以便找出该值是否是一个特殊的一个或不

"Quote" is waving a big red flag that says "hey buddy, look over here, I'm a nested lambda expression and I have wacky semantics if I'm closed over an outer variable!" whereas "Constant" is saying "I'm nothing more than a value; use me as you see fit." When something is complicated and dangerous we want to be making it wave red flags, not hiding that fact by making the user dig through the type system in order to find out whether this value is a special one or not.

此外,想法避免冗余甚至一个目标是不正确。当然,避免不必要的,混乱的冗余是一个目标,但大多数冗余是一件好事;冗余创建清晰度。新的工厂方法和节点品种是的便宜的。因为我们需要让每个人重新presents一个操作干净,我们可以使尽可能多的。我们有没有必要求助于讨厌的小把戏,比如这意味着一件事,除非这个字段设置为这件事情,在这种情况下,它意味着别的东西。

Furthermore, the idea that avoiding redundancy is even a goal is incorrect. Sure, avoiding unnecessary, confusing redundancy is a goal, but most redundancy is a good thing; redundancy creates clarity. New factory methods and node kinds are cheap. We can make as many as we need so that each one represents one operation cleanly. We have no need to resort to nasty tricks like "this means one thing unless this field is set to this thing, in which case it means something else."

这篇关于什么防爆pression.Quote()做防爆pression.Constant()已经不能做什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆