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

Tof*_*Bug 8 c# lambda expression-trees

我已经过了一段时间,靠着墙头撞了一会儿,现在搜索各种短语和关键词,但我找不到任何接近答案的内容,所以我希望有人在这里可以解决一些问题.

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

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

如果我写这样的东西

Expression<Func<string,string>> InsertAString = (Insert) => "This " + (Insert == "" ? "" : Insert + " ") + "That";
Run Code Online (Sandbox Code Playgroud)

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

  • F(NodeType = Lambda)
    • 正文(NodeType = Add)
      • 左(NodeType = Add)
        • 左(NodeType =常量,值="此")
        • 对(NodeType =条件)
          • IfFalse(NodeType = Add)
            • 左(NodeType =参数,名称="插入")
            • 右(NodeType =常量,值="")
          • IfTrue(NodeType = Constant,Value ="")
          • 测试(NodeType = Equal)
            • 左(NodeType =参数,名称="插入")
            • 右(NodeType =常量,值="")
      • 右(NodeType =常数,值="那")
    • 参数(计数= 1)
      • 参数[0](NodeType =参数,名称="插入")

我可以打电话

Console.WriteLine(InsertAString.Compile()("Is Something In-between"));
Run Code Online (Sandbox Code Playgroud)

我按照我的期望离开了

"这是介于两者之间的事情"

现在,如果我尝试使用Expression基类的静态方法手动重建,我遇到了一个有趣的问题.(为了调试目的,我已将每一步分解为自己的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>>   
Run Code Online (Sandbox Code Playgroud)

基于上面生成的表达式树的值,重新创建与上面相同的基本表达式树(至少使用相同的"Look")

一切都很好,直到你到达这条线

BinaryExpression InsertPlusSpace = Expression.Add(Insert,Space);
Run Code Online (Sandbox Code Playgroud)

编译器抛出InvalidOperationException未处理

没有为'System.String'和'System.String'定义二进制运算符Add

现在为什么这样?

为什么当我让C#将Lambda转换为Expression时,它显然使用Add NodeType,而Types显示它肯定使用System.String但是当我尝试手动执行相同操作时它不会让代码继续吗?

作为最后一点,我甚至尝试了以下内容:

BinaryExpression InsertPlusSpace = Expression.MakeBinary( ExpressionType.Add,Insert,Space);
Run Code Online (Sandbox Code Playgroud)

同样的错误.

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

提前感谢大家的回复.

Tho*_*que 8

检查文档:实际上没有在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 });
Run Code Online (Sandbox Code Playgroud)

(注意,这methodof不是一个实际的运算符,它正是Reflector为ldtokenIL指令显示的内容.在C#中,你必须使用反射检索方法.)

  • +1,但为了清楚起见,一个简洁的例子:`Expression.Add(Expression.Constant("a"),Expression.Constant("b"),typeof(string).GetMethod("Concat",new [] {typeof (string),typeof(string)}));` (3认同)