为什么不同版本的.net(或编译器)为同一个表达式生成不同的表达式树

时间:2016-04-29 17:06:12

标签: c# expression-trees system.reflection

在我的一个库中,我有从表达式返回MethodInfo的代码:

public MethodInfo GetMethod(Expression expression)
{
    var lambdaExpression = (LambdaExpression)expression;
    var unaryExpression = (UnaryExpression)lambdaExpression.Body;
    var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
    var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last();
    return (MethodInfo)methodInfoExpression.Value;
}

我有一系列辅助函数来启用如下调用:

public MethodInfo Func<T, R, A1>(Expression<Func<T, Func<A1, R>>> expression)
{
    return GetMethod(expression);
}

这将启用以下语法:

var methodInfo = Func<TestClass, bool, string>(x => x.AnInstanceMethodThatTakesAStringAndReturnsABool);

这很好用,直到我最近将库升级到.Net 4.6.1和最新的c#编译器。

在.net的早期版本中,表达式的格式为:

{x => Convert(CreateDelegate(System.Func`2[System.String, System.Boolean], x, Boolean AnInstanceMethodThatTakesAStringAndReturnsABool(System.String)))}

因此我的代码会将methodInfoExpression作为methodCallExpression的最后一个参数。

现在,在.Net 4.6.1(最新的c#编译器)中,编译器似乎正在生成一个不同形式的表达式:

{x => Convert(Boolean AnInstanceMethodThatTakesAStringAndReturnsABool(System.String).CreateDelegate(System.Func`2[System.String, System.Boolean], x))}

我当前的代码中断了,因为最后一个参数不是ConstantExpression。看着它 - 轻松修复,只需更改为

 var methodInfoExpression = (ConstantExpression)methodCallExpression.Object;

显然,GetMethod函数非常脆弱,可能会改变编译器生成表达式的方式。我很好奇这个更改的原因以及我如何重构GetMethod,以便它对编译器生成的表达式树更具弹性。

1 个答案:

答案 0 :(得分:4)

  

我很好奇改变的原因

从.NET 4.5开始,有两种方法可以从MethodInfo创建委托:

  1. MethodInfo
  2. 上的实例方法
  3. Delegate.CreateDelegate静态方法
  4. 看起来微软决定从使用#2切换到使用#1,无论出于何种原因(它可能更有效)。

      

    我如何重构GetMethod以便它对编译器生成的表达式树更具弹性?

    您可以使用expression tree visitor,并以这种方式查找方法信息。