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

问题描述:

在我的图书馆之一,我有一个从表达式返回的MethodInfo代码:为什么不同版本的.NET(或编译器)的生成相同的表达不同的表达式树

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的最后一个参数。

现在,在净4.6.1(最新的C#编译器),编译器似乎产生不同形式的表达:因为最后一个参数是不是一个常量表达式

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

我当前的代码中断。看着它 - 很容易解决,只需改变

var methodInfoExpression = (ConstantExpression)methodCallExpression.Object; 

显然,实现getMethod功能是相当脆弱的,可作改动编译器如何生成表达式。我很好奇改变的原因,以及我如何重构GetMethod,以便它对编译器生成的表达式树更有弹性。

+1

你应该真的发布示例代码,在提供示例时进行编译。如果您希望帮助更有效地编写该方法,则应实际描述需要执行的操作,而不是仅显示其非工作版本。 – Servy

我很好奇,为改变

随着.NET 4.5的原因有来自MethodInfo作出委托的方式有两种:

  1. 实例方法对MethodInfo ,和
  2. Delegate.CreateDelegate静态方法

看起来像微软决定从使用#2切换到使用#1,无论出于何种原因(它可能更有效)。

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

您可以使用expression tree visitor,然后查找方法信息。

+0

非常有帮助。谢谢! –