views:

55

answers:

1

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: http://stackoverflow.com/questions/2215712/c-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.

Thanks.

-- Brian

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?

+2  A: 

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.

Marc Gravell