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

问题描述 投票:0回答:1

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

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

我遇到了一个我无法理解的奇怪异常现象

如果我写这样的东西

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

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

  • F(节点类型 = Lambda)
    • 正文(NodeType = 添加)
      • 左(节点类型 = 添加)
        • Left (NodeType = Constant, Value = "This ")
        • 右(NodeType = 条件)
          • IfFalse(节点类型 = 添加)
            • 左(节点类型=参数,名称=“插入”)
            • 右(NodeType = Constant,Value =“”)
          • IfTrue (NodeType = Constant, Value = "")
          • 测试(节点类型=相等)
            • 左(节点类型=参数,名称=“插入”)
            • 右(NodeType = Constant,Value =“”)
      • Right(NodeType = 常量,Value = “那个”)
    • 参数(计数 = 1)
      • 参数[0](节点类型=参数,名称=“插入”)

我可以打电话

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

然后我如期而至

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

现在,如果我尝试使用 Expression 基类的静态方法手动重建它,我会遇到一个有趣的问题。 (为了调试目的,我已经将每个步骤分解成自己的表达式)

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

编译器抛出一个 InvalidOperationException 原为 未处理

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

现在这是为什么?

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

作为最后的说明,我什至尝试了以下内容:

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

同样的错误。

我很好奇为什么看起来至少到目前为止我已经能够找到表达式树中的字符串连接仅在不尝试手动构建表达式树以添加 System.String 类型的常量和变量时才有效

c# lambda expression-trees
1个回答
9
投票

查看文档:'+'运算符实际上没有在

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# 中,您必须使用反射来检索方法。)

© www.soinside.com 2019 - 2024. All rights reserved.