Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to set 'DeclaringType' in an expression tree?

I am doing a Func -> Expression -> Func conversion. It works fine if I create the Func<>() from a method(first example below) however if I create the function using an expression tree(2nd example) it fails with a NullReferenceException when accessing func2.Method.DeclaringType.FullName. And this is because DeclaringType is null. (NJection uses reflection so I think that is why it needs DeclaringType.)

How can I fill in DeclaringType type for the Func<> that was created by compiling an Expression Tree? (maybe its not possible?) DeclaringType is set in the first example.

Using a Func<> from a Method... (works well)

// Build a Func<> 
Func<int, int> add = Add;
// Convert it to an Expression using NJection Library
Expression<Func<int, int>> expr = ToExpr<Func<int, int>>(add);
// Convert it back to a Func<>
Func < int, int> func = expr.Compile();
// Run the Func<>
int result = func(100);

Using an Expression tree (does not work)...

// Build a Func<> using an Expression Tree
ParameterExpression numParam = Expression.Parameter(typeof(int));
ConstantExpression five = Expression.Constant(5, typeof(int));
BinaryExpression numAddFive = Expression.Add(numParam, five);
Func<int, int> func2 = 
    Expression.Lambda<Func<int, int>>(
        numAddFive, 
        new ParameterExpression[] { numParam }).Compile();
// Convert to an Expression using NJection (EXCEPTION ON NEXT LINE)
// ToExpr is trying to access func2.Method.DeclaringType.FullName(DeclaringType is null)
Expression<Func<int, int>> exp2 = ToExpr<Func<int, int>>(func2);
// Convert it back to a Func<>
Func<int, int> func2b = exp2.Compile();
// Run the Func<>
int result2 = func2b(100);
like image 608
SunsetQuest Avatar asked Dec 14 '15 22:12

SunsetQuest


2 Answers

I don't know what NJection library is used for since their website is down and there is no source code available at their Codeplex.

If you just need to get an Expression that you can compile back to the function, you can create it yourself. E.g. like this:

static Expression<T> ToExpr<T>(T func)
{
    var type = typeof(T);
    var method = type.GetMethod("Invoke"); // Delegate.Invoke() has parameters types matching delegate parameters types
    var pars = method.GetParameters()
        .Select(pi => Expression.Parameter(pi.ParameterType))
        .ToArray();

    return Expression.Lambda<T>(
        Expression.Call(Expression.Constant(func), method, pars),
        pars
    );
}

With this ToExpr, your above code compiles and runs without problems.

like image 189
torvin Avatar answered Sep 19 '22 16:09

torvin


I'm guessing no.

When you compile an expression to an executable Func, it's technically a dynamic method. You can see this if you were to call func2.Method.GetType(). That returns type DynamicMethod which always has null as the Declaring Type.

like image 43
Shlomo Avatar answered Sep 18 '22 16:09

Shlomo