Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do different versions of .net (or the compiler) generate different expression trees for the same expression

Tags:

In one of my libraries, I have code that returns a MethodInfo from an expression:

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

I had a series of helper functions to enable calls like the following:

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

This would enable the following syntax:

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

This worked great, until I recently upgraded the library to .Net 4.6.1 and the latest c# compiler.

In the previous version of .net, the expression would be of the form:

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

Hence my code would look for the methodInfoExpression as the last argument of the methodCallExpression.

Now, in .Net 4.6.1 (latest c# compiler), the compiler seems to be producing an expression of a different form:

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

My current code breaks because the last argument is not a ConstantExpression. Looking at it - easy fix, just change to

 var methodInfoExpression = (ConstantExpression)methodCallExpression.Object;

Obviously, the GetMethod function is pretty fragile and subject to changes to how the compiler generates expressions. I'm curious as to the reason for the change and how I might refactor GetMethod so that it is more resilient to the expression tree generated by the compiler.

like image 228
Joe Enzminger Avatar asked Apr 29 '16 17:04

Joe Enzminger


People also ask

Why do we need expression trees C#?

When you want to have a richer interaction, you need to use Expression Trees. Expression Trees represent code as a structure that you can examine, modify, or execute. These tools give you the power to manipulate code during run time. You can write code that examines running algorithms, or injects new capabilities.

What is the use of expression tree?

The main use of these expression trees is that it is used to evaluate, analyze and modify the various expressions. It is also used to find out the associativity of each operator in the expression. For example, the + operator is the left-associative and / is the right-associative.

What are the advantages of expression tree?

Expression trees allow you to build code dynamically at runtime instead of statically typing it in the IDE and using a compiler. They are well explained in the documentation.

What is expression trees and how they used in LINQ?

Expression Trees was first introduced in C# 3.0 (Visual Studio 2008), where they were mainly used by LINQ providers. Expression trees represent code in a tree-like format, where each node is an expression (for example, a method call or a binary operation such as x < y).


1 Answers

I'm curious as to the reason for the change

As of .NET 4.5 there were two ways of making a delegate from MethodInfo:

  1. An instance method on MethodInfo, and
  2. Delegate.CreateDelegate static method

It looks like Microsoft decided to switch from using #2 to using #1, for whatever reason (it's probably more efficient).

how I might refactor GetMethod so that it is more resilient to the expression tree generated by the compiler?

You could use expression tree visitor, and look for the method info that way.

like image 117
Sergey Kalinichenko Avatar answered Sep 28 '22 02:09

Sergey Kalinichenko