dynamic method invocation in expression tree

995 views Asked by At

When constructing an expression tree, I have to use nodes invoking external methods in order to obtain values the expression could then continue evaluation with. These methods are supplied as Func<T> and my code has no knowledge of where they originate from.

What is the most correct way of performing the mentioned invocation? I've tried something like this:

private Dictionary<string, Delegate> _externalSymbols;

private Expression _forExternalSymbol(string identifier)
{
    Delegate method = _externalSymbols[identifier];
    return Expression.Call(method.Method);
}

which works as long as the method fetched from the dictionary was created in compile-time. However, in case of Func<T> being a dynamic method obtained, for instance, by compiling another expression in runtime, this won't work out throwing

ArgumentException: Incorrect number of arguments supplied for call to method 'Int32 lambda_method(System.Runtime.CompilerServices.ExecutionScope)'

The desired effect may be achieved by wrapping the given function into one extra expression, but that seems quite hideous comparing to what it used to look like:

private Expression _forExternalSymbol(string identifier)
{
    Delegate method = _externalSymbols[identifier];
    Expression mediator = method is Func<double> ?
        (Expression)(Expression<Func<double>>)(() => ((Func<double>)method)()) :
        (Expression<Func<string>>)(() => ((Func<string>)method)());
    return Expression.Invoke(mediator);
}

Also, this is hardly an extensible approach should I need to add support for types other than double and string.

I would like to know if there are better options which would work with dynamically created methods (preferably applicable to .NET 3.5).

1

There are 1 answers

1
svick On BEST ANSWER

which works as long as the method fetched from the dictionary was created in compile-time

No, it works as long as the method is static. For example, it also won't work if the delegate is a lambda that references something from its parent score (i.e. it's a closure).

The correct way to invoke a delegate is to use Expression.Invoke(). To get an Expression that represents your delegate, use Expression.Constant():

Expression.Invoke(Expression.Constant(method)))