Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating an expression tree that calls a method

Is it possible to create an expression tree that directly calls a method? For example, consider the following method:

public static int MyFunc(int a, int b)
{
    return a + b;
}

I would like to create an expression tree that calls MyFunc with parameters a=1 and b=2. One way to accomplish this is with reflection:

var c1 = Expression.Constant(1);
var c2 = Expression.Constant(2);
var expr = Expression.Call(typeof(Program).GetMethod("MyFunc"), c1, c2);

However, this is disadvantageous because reflection is slow and turns what should be compile-time errors into run-time errors.

I could use the following approach instead:

Expression<Func<int, int, int>> lambda = (a, b) => MyFunc(a, b);
var expr = Expression.Invoke(lambda, c1, c2);

But this is still not what I want because it wraps the method in a lambda expression instead of calling it directly.

A good solution might be based on a delegate, like this:

Func<int, int, int> del = Program.MyFunc;
var expr = Expression.Invoke(del, c1, c2);

Unfortunately, that does not compile because del is a delegate rather than an expression. Is there any way to build an expression from a delegate? (Note that I know the target of the delegate at compile-time, so I don't need the kind of flexibility described here: Expression Trees and Invoking a Delegate.)

A non-delegate solution would also be fine, as long as it calls the target method as directly as possible.

Update: This also works, but it still relies on reflection:

Func<int, int, int> del = Program.MyFunc;
var expr = Expression.Call(del.Method, c1, c2);

At least it is more likely to catch problems at compile-time. But it still pays the run-time price for reflection, doesn't it?

like image 279
Brian Berns Avatar asked Jul 26 '10 04:07

Brian Berns


People also ask

Can you have an expression in a method call?

A method call expression produces the pure value returned by the method; the type of this value is specified by the return type in the method declaration. But if the method has the return type void, the expression does not produce a value.

What is method call expression?

Object. Gets the Expression that represents the instance for instance method calls or null for static method calls. Type. Gets the static type of the expression that this Expression represents.

What is a LINQ expression tree?

Expression Trees provide richer interaction with the arguments that are functions. You write function arguments, typically using Lambda Expressions, when you create LINQ queries. In a typical LINQ query, those function arguments are transformed into a delegate the compiler creates.


1 Answers

As long as you call .Compile on the lambda and store (and re-use) the delegate, you only pay the reflection price once.

var c1 = Expression.Constant(1);
var c2 = Expression.Constant(2);
var expr = Expression.Call(typeof(Program).GetMethod("MyFunc"), c1, c2);
Func<int> func = Expression.Lambda<Func<int>>(expr).Compile();
// ** now store func and re-use it **

However, to get a naked delegate to just that method, you can use:

var method = typeof(Program).GetMethod("MyFunc");
Func<int, int, int> func = (Func<int, int, int>) Delegate.CreateDelegate(
         typeof(Func<int, int, int>), method);

of course, then you are forced to provide the constants at the caller.

Another option is DynamicMethod, but as long as you are caching the final delegate, this won't be significantly faster. It does offer more flexibility (at the price of complexity), but that doesn't seem to be the issue here.

like image 165
Marc Gravell Avatar answered Sep 28 '22 02:09

Marc Gravell