为什么我不能手动创建与直接 lambda 生成的相同的表达式树
我已经把头撞在墙上一段时间了,现在搜索了各种短语和关键词,但我找不到任何接近答案的东西,所以我希望这里有人能提供一些线索。
基本上,我正在深入研究 C# 4.0 中的操作、创建和修改表达式树,
我无法理解
我遇到了一个奇怪的异常,如果我写这样的东西,
Expression<Func<string,string>> InsertAString = (Insert) => "This " + (Insert == "" ? "" : Insert + " ") + "That";
当我进行调试并查看它看起来的表达式树时类似于此
- F (NodeType = Lambda)
- 正文(节点类型 = 添加)
- 左(节点类型 = 添加)
- 左(NodeType = 常量,值 =“This”)
- 右(节点类型 = 有条件)
- IfFalse(节点类型 = 添加)
- 左(节点类型=参数,名称=“插入”)
- 右(节点类型=常量,值=“”)
- IfTrue(节点类型=常量,值=“”)
- 测试(NodeType = 等于)
- 左(节点类型=参数,名称=“插入”)
- 右(NodeType = 常量,值 = "")
- IfFalse(节点类型 = 添加)
- 右(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 = "")
- IfFalse (NodeType = Add)
- Right (NodeType = Constant, Value = "That")
- Left (NodeType = Add)
- Paramerters (Count = 1)
- Parameters[0] (NodeType = Parameter, Name = "Insert")
- Body (NodeType = Add)
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
unhandledThe 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
检查文档:“+”运算符实际上没有在
String
类中定义。我猜编译器只是知道它的意思是“连接字符串”,并将其转换为对Concat
的调用。因此,当您调用Expression.Add
时,您需要指定实现该操作的方法(在这种情况下为String.Concat
方法)。我用 Reflector 反编译了表达式,它给出了以下结果(重新格式化):(
请注意,
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 toConcat
. So when you callExpression.Add
, you need to specify the method that implements the operation (in that case theString.Concat
method).I decompiled the expression with Reflector, it gives the following result (reformatted):
(Note that
methodof
is not an actual operator, its just what Reflector shows for theldtoken
IL instruction. In C# you have to retrieve the method using reflection.)