Expression Trees and Invoking a Delegate

22,573

Solution 1

OK, this shows how it can be done (but it is very inelegant in my opinion):

Func<int, int> func = null;
Expression<Func<int, int>> bind = (x) => func(x);

Expression expr = Expression.Invoke(bind, Expression.Constant(5));

Expression<Func<int>> lambda = Expression.Lambda<Func<int>>(expr);
Func<int> compiled = lambda.Compile();

Console.WriteLine(expr);

func = x => 3 * x;
Console.WriteLine(compiled());

func = x => 7 * x;
Console.WriteLine(compiled());

Console.Read();

Essentially I use (x) => func(x); to make a function that calls what the delegate points to. But you can see that expr is overly complicated. For this reason I don't consider this answer good, but maybe it can be built upon?

Solution 2

I think what you want to do is use the Target and Method properties of the delegate to pass to create a Call expression. Building on JulianR's sample, this is what it would look like:

Action<int> func = i => Console.WriteLine(i * i);

var callExpr = Expression.Call(Expression.Constant(func.Target), func.Method, Expression.Constant(5));

var lambdaExpr = Expression.Lambda<Action>(callExpr);
var fn = lambdaExpr.Compile();
fn();    //  Prints 25

Solution 3

While other answers provide some working ways there's a shorter one:

Expression.Invoke(Expression.Constant(my_delegate), parameter_for_delegate)

It works both for delegates referencing static methods and instance methods with no change.

Solution 4

This should work:

Action<int> func = i => Console.WriteLine(i * i);

// If func is null like in your example, the GetType() call fails, 
// so give it a body or use typeof if you know the type at compile time
var param = Expression.Parameter(func.GetType());

// Call the Invoke method on the delegate, which is the same as invoking() it
var callExpr = Expression.Call(param, func.GetType().GetMethod("Invoke"), Expression.Constant(5)); 

var lambdaExpr = Expression.Lambda<Action<Action<int>>>(callExpr, param); 

var fn = lambdaExpr.Compile(); // Compile the expression tree so it can be executed 

fn(func); // Prints 25

Expressions can be a mindfuck, but remember: expressions are always built up out of other expressions. An expression is a tree of other expressions that describes code. You can't pass in the actual delegate like you do in your example, what you need is an expression of that delegate, by saying the expression expects a parameter of the type of your delegate. Then you say you want to call a method on that parameter, namely the Invoke method, with the argument '5'. All the other stuff after that is just if you want to turn the expression into runnable code, which you probably do.

I ran this with .NET4 though, I hope I haven't mixed in .NET4 only expression stuff.

EDIT In response to PythonPower's comment:

I think what you want (not passing in the delegate as an argument) can only be done when the delegate itself is described as an expression, like this:

 var arg = Expression.Parameter(typeof(int), "i");

 var multiply = Expression.Multiply(arg, arg);

 var writeln = Expression.Call(typeof(Console).GetMethod("WriteLine", 
   new[] { typeof(int) }), multiply);

 var lambda = Expression.Lambda<Action<int>>(writeln, arg);

 var compiled = lambda.Compile();

 compiled(5); // Prints 25

The only other way I can think of is to capture an delegate declared locally in a closure, but I wouldn't know how to do that.

Share:
22,573

Related videos on Youtube

abatishchev
Author by

abatishchev

This is my GUID. There are many like it but this one is mine. My GUID is my best friend. It is my life. I must master it as I must master my life. Without me, my GUID is useless. Without my GUID I am useless.

Updated on August 21, 2020

Comments

  • abatishchev
    abatishchev over 3 years

    So I have a delegate which points to some function which I don't actually know about when I first create the delegate object. The object is set to some function later.

    I also then want to make an expression tree that invokes the delegate with an argument (for this question's sake the argument can be 5). This is the bit I'm struggling with; the code below shows what I want but it doesn't compile.

    Func<int, int> func = null;
    Expression expr = Expression.Invoke(func, Expression.Constant(5));
    

    For this example I could do (this is practical since I need to build the expression trees at runtime):

    Func<int, int> func = null;
    Expression<Func<int>> expr = () => func(5);
    

    This makes expr become:

    () => Invoke(value(Test.Program+<>c__DisplayClass0).func, 5)
    

    Which seems to mean that to use the delegate func, I need to produce the value(Test.Program+<>c__DisplayClass0).func bit.

    So, how can I make an expression tree which invokes a delegate?

    • Manish Basantani
      Manish Basantani about 14 years
      Linq in Action has a detailed section over Expression trees.
  • Admin
    Admin about 14 years
    This is very close to what I want, but I do not want to pass the delegate as an argument.
  • Admin
    Admin about 14 years
    I have to replace Expression.Constant(func.Target) with null to get it to work. But that binds to what the function func points to currently not what it may point to later. The problem is that no assumption on what func points to can be made and it may change at any time.
  • Admin
    Admin about 14 years
    The edit assumes I know the function but I don't know the function until after the Expression is created. And the function isn't necessarily an expression itself. The closure idea sounds promising.
  • Daniel Plaisted
    Daniel Plaisted about 14 years
    The expression you create needs to have a way to access the func variable by reference. You can't create a reference to a local variable. In your code you are using the bind lambda to capture the local variable, and the C# compiler works its magic and creates a separate class to hold what looks like a local variable. You could do this yourself and you wouldn't have to use the bind expression, but the resulting lambda would probably be just as complicated.
  • Admin
    Admin about 14 years
    I would be interested in seeing the "do it yourself method". I imagine it would require some magic from Reflection.Emit?
  • ironic
    ironic over 10 years
    This is almost the answer I needed, but please remove first parameter from Expression.Call. Otherwise, there is an exception (I checked on both .NET4 and .NET4.5)
  • Daniel Plaisted
    Daniel Plaisted over 10 years
    @ironic: Was the target method a static method when you were testing? That might be the difference.
  • ironic
    ironic over 10 years
    The target was actually one from your example: i => Console.WriteLine(i * i); , which IS static, since it does not reference anything.
  • Nic3500
    Nic3500 over 5 years
    While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value.
  • Filip Navara
    Filip Navara over 5 years
    @Nic3500 I definitely agree, but I don't feel like the right person to expand on it. I just noticed that there's plenty of answers that work to some extent, but that don't work universally. The top-voted answer doesn't work for delegates referencing static methods iirc. The accepted answer is plain too complicated. I changed the answer to "community wiki" to allow someone to expand on it.