为什么我不能手动创建与直接 lambda 生成的相同的表达式树

发布于 2024-09-26 17:05:42 字数 2730 浏览 0 评论 0原文

我已经把头撞在墙上一段时间了,现在搜索了各种短语和关键词,但我找不到任何接近答案的东西,所以我希望这里有人能提供一些线索。

基本上,我正在深入研究 C# 4.0 中的操作、创建和修改表达式树,

我无法理解

我遇到了一个奇怪的异常,如果我写这样的东西,

Expression<Func<string,string>> InsertAString = (Insert) => "This " + (Insert == "" ? "" : Insert + " ") + "That";

当我进行调试并查看它看起来的表达式树时类似于此

  • F (NodeType = Lambda)
    • 正文(节点类型 = 添加)
      • 左(节点类型 = 添加)
        • 左(NodeType = 常量,值 =“This”)
        • 右(节点类型 = 有条件)
          • IfFalse(节点类型 = 添加)
            • 左(节点类型=参数,名称=“插入”)
            • 右(节点类型=常量,值=“”)
          • IfTrue(节点类型=常量,值=“”)
          • 测试(NodeType = 等于)
            • 左(节点类型=参数,名称=“插入”)
            • 右(NodeType = 常量,值 = "")
      • 右(NodeType = 常量,值 =“That”)
    • 参数(计数 = 1)
      • 参数[0](节点类型 = 参数,名称 =“插入”)

我可以调用

Console.WriteLine(InsertAString.Compile()("Is Something In-between"));

并且我会按照我的预期退出

“这是介于两者之间的东西”

现在,如果我尝试使用表达式库的静态方法手动重建它课堂上我遇到了一个有趣的问题。 (出于调试目的,我已将每个步骤分解为自己的表达式)

ParameterExpression Insert = Expression.Parameter(typeof(object), "Insert");
ConstantExpression This = Expression.Constant("This ");
ConstantExpression That = Expression.Constant("That");
ConstantExpression Space = Expression.Constant(" ");
ConstantExpression NoCharacter = Expression.Constant("");
BinaryExpression InsertPlusSpace = Expression.Add(Insert,Space);
BinaryExpression InsertEqualsNoCharacter = Expression.Equal(Insert,NoCharacter);
ConditionalExpression InsertPlusSpaceOrNothing = Expression.IfThenElse(InsertEqualsNoCharacter,NoCharacter,InsertPlusSpace);
BinaryExpression ThisPlusInsertPlusSpaceOrNothing = Expression.Add(This,InsertPlusSpaceOrNothing);
BinaryExpression ThisPlusInsertPlusSpaceOrNothingPlusThat = Expression.Add(ThisPlusInsertPlusSpaceOrNothing, That);
Lambda Lambda = Expression.Lambda(ThisPlusInsertPlusSpaceOrNothingPlusThat, Middle);
Expression<Func<string,string>> InsertAString = Lambda as Expression<Func<string,string>>   

基于上面生成的表达式树的值重新创建与上面相同的基本表达式树(至少具有相同的“外观”)

一切都会顺利进行,直到您到达这条线

BinaryExpression InsertPlusSpace = Expression.Add(Insert,Space);

编译器抛出一个 无效操作异常原为 未处理

二元运算符 Add 未定义 对于“System.String”和 '系统.字符串'

这是为什么呢?

为什么当我让 C# 将 Lambda 转换为表达式时,它显然使用了 Add NodeType,并且类型显示显示它肯定使用 System.String,但当我尝试手动执行相同操作时,它不会让代码继续执行?

最后一点,我什至尝试过以下操作:

BinaryExpression InsertPlusSpace = Expression.MakeBinary( ExpressionType.Add,Insert,Space);

同样的错误。

我很好奇为什么至少在我到目前为止所发现的情况下,表达式树中的字符串连接只有在不尝试手动构建添加 System.String 类型的常量和变量的表达式树时才有效。

I've gone through and beat my head against the wall for a while now searched on various phrases and keywords but I cannot find anything close to an answer so i'm hoping someone here can shed some light.

Basically I'm working on diving pretty deep into manipulating, creating, and modifying Expression Trees in C# 4.0

I came across an odd anomaly I cannot make sense of

if I write something like this

Expression<Func<string,string>> InsertAString = (Insert) => "This " + (Insert == "" ? "" : Insert + " ") + "That";

When I get debug and look at the expression tree it looks similar to this

  • F (NodeType = Lambda)
    • Body (NodeType = Add)
      • Left (NodeType = Add)
        • Left (NodeType = Constant, Value = "This ")
        • Right (NodeType = Conditional)
          • IfFalse (NodeType = Add)
            • Left (NodeType = Parameter, Name = "Insert")
            • Right (NodeType = Constant, Value = " ")
          • IfTrue (NodeType = Constant, Value = "")
          • Test (NodeType = Equal)
            • Left (NodeType = Parameter, Name = "Insert")
            • Right (NodeType = Constant, Value = "")
      • Right (NodeType = Constant, Value = "That")
    • Paramerters (Count = 1)
      • Parameters[0] (NodeType = Parameter, Name = "Insert")

I can call

Console.WriteLine(InsertAString.Compile()("Is Something In-between"));

And I get out as I expect

"This is something In-between That"

Now if i try and rebuild that manually using the static methods of the Expression base class I run into an interesting issue. (I have broken down each step into its own Expression for debugging purposes)

ParameterExpression Insert = Expression.Parameter(typeof(object), "Insert");
ConstantExpression This = Expression.Constant("This ");
ConstantExpression That = Expression.Constant("That");
ConstantExpression Space = Expression.Constant(" ");
ConstantExpression NoCharacter = Expression.Constant("");
BinaryExpression InsertPlusSpace = Expression.Add(Insert,Space);
BinaryExpression InsertEqualsNoCharacter = Expression.Equal(Insert,NoCharacter);
ConditionalExpression InsertPlusSpaceOrNothing = Expression.IfThenElse(InsertEqualsNoCharacter,NoCharacter,InsertPlusSpace);
BinaryExpression ThisPlusInsertPlusSpaceOrNothing = Expression.Add(This,InsertPlusSpaceOrNothing);
BinaryExpression ThisPlusInsertPlusSpaceOrNothingPlusThat = Expression.Add(ThisPlusInsertPlusSpaceOrNothing, That);
Lambda Lambda = Expression.Lambda(ThisPlusInsertPlusSpaceOrNothingPlusThat, Middle);
Expression<Func<string,string>> InsertAString = Lambda as Expression<Func<string,string>>   

That based on the values of the generated Expression tree above recreate the same basic expression tree as above (at least with the same "Look")

Everything steps through fine until you get to this line

BinaryExpression InsertPlusSpace = Expression.Add(Insert,Space);

The compiler throws an
InvalidOperationException was
unhandled

The binary operator Add is not defined
for 'System.String' and
'System.String'

Now why is this?

Why when I let C# convert a Lambda into an Expression does it obviously use the Add NodeType, and the Types display show it is definitely using System.String yet when i try and do the same manually it will not let the code continue?

As a Final note I've even tried the following:

BinaryExpression InsertPlusSpace = Expression.MakeBinary( ExpressionType.Add,Insert,Space);

Same error.

I'm curious why it seems at least with what i have been able to find so far that string concatenation in expression trees works only if are not trying to build an expression tree manually that adds constants and variables of type System.String.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

回忆躺在深渊里 2024-10-03 17:05:42

检查文档:“+”运算符实际上没有在 String 类中定义。我猜编译器只是知道它的意思是“连接字符串”,并将其转换为对 Concat 的调用。因此,当您调用Expression.Add时,您需要指定实现该操作的方法(在这种情况下为String.Concat方法)。

我用 Reflector 反编译了表达式,它给出了以下结果(重新格式化):(

ParameterExpression expression2;
Expression<Func<string, string>> expression =
    Expression.Lambda<Func<string, string>>(
        Expression.Add(
            Expression.Add(
                Expression.Constant("This ", typeof(string)),
                Expression.Condition(
                    Expression.Equal(
                        expression2 = Expression.Parameter(typeof(string), "Insert"),
                        Expression.Constant("", typeof(string)),
                        false,
                        (MethodInfo) methodof(string.op_Equality)),
                    Expression.Constant("", typeof(string)),
                    Expression.Add(
                        expression2,
                        Expression.Constant(" ", typeof(string)),
                        (MethodInfo) methodof(string.Concat))),
                (MethodInfo) methodof(string.Concat)),
            Expression.Constant("That", typeof(string)),
            (MethodInfo) methodof(string.Concat)),
        new ParameterExpression[] { expression2 });

请注意,methodof 不是实际的运算符,它只是 Reflector 针对 ldtoken IL 指令显示的内容在 C# 中,您必须使用反射来检索方法。)

Check the documentation: the '+' operator is actually not defined in the String class. I guess the compiler just knows it means "concatenate the strings", and it transforms it into a call to Concat. So when you call Expression.Add, you need to specify the method that implements the operation (in that case the String.Concat method).

I decompiled the expression with Reflector, it gives the following result (reformatted):

ParameterExpression expression2;
Expression<Func<string, string>> expression =
    Expression.Lambda<Func<string, string>>(
        Expression.Add(
            Expression.Add(
                Expression.Constant("This ", typeof(string)),
                Expression.Condition(
                    Expression.Equal(
                        expression2 = Expression.Parameter(typeof(string), "Insert"),
                        Expression.Constant("", typeof(string)),
                        false,
                        (MethodInfo) methodof(string.op_Equality)),
                    Expression.Constant("", typeof(string)),
                    Expression.Add(
                        expression2,
                        Expression.Constant(" ", typeof(string)),
                        (MethodInfo) methodof(string.Concat))),
                (MethodInfo) methodof(string.Concat)),
            Expression.Constant("That", typeof(string)),
            (MethodInfo) methodof(string.Concat)),
        new ParameterExpression[] { expression2 });

(Note that methodof is not an actual operator, its just what Reflector shows for the ldtoken IL instruction. In C# you have to retrieve the method using reflection.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文