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

2

6

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);
Anschluss answered 14/12, 2015 at 22:9 Comment(5)
What are you trying to achieve with that ToExpr<Func<int, int>>(func2) anyway? What kind of Expression are you expecting to get back?Lagging
I am still learning linq/labda/etc. My goal is to simplify an expression expression program(or tree). Maybe I can have the compiler compile an expression to code then take that code and convert it back to an expression tree. I would guess the resulting expression tree would be a reduced version of the original. So if I have a expression tree of a+a+a+2+4+3 then I compile it and convert that code back into an expression I would get (3*a)+9. (I would guess) I said Func -> Expr -> Fun but the real goal is an Expr -> Func(code) -> Expr. If I can do the first then I think I can do the later.Anschluss
Why do you want to simplify the expression? If you are just going to compile and run in afterwards, the compiler still will do all the optimizations to it, so do not bother. If you are trying to use the compiler to simplify expressions for you for some other task - you probably won't achieve much, since your idea of 'simple' could be quite different from the compiler's.Lagging
Two reasons: It is a science project. =) I am planning on generating random functions and then later modifying those functions slightly to create a new function. Overtime there will be lots of junk and simplifications can be done every now and then to keep this junks clean so it will hopefully compile quicker. (something like genetic algorithms) The second reason is it would be good if there was a way to simplify any block expression using the optimization abilities of the C# compiler!Anschluss
Just so you know, C# compiler does little to no optimizations. The main part is done by JIT (i.e. during converting IL to native code), so libs like NJection won't help you...Lagging
L
4

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.

Lagging answered 15/12, 2015 at 0:6 Comment(4)
The library is here github.com/sagifogel/NJection.LambdaConverter. I copied the "ExecuteLambda" method then remove the final ".compile()" and saving it as a 'ToExpr' method. Your method works for my example code but I noticed 'expr.body' has an 'Invoke' and not a 'block' with expressions in it like the 'NJection' produces. I might not know what I am talking about. =) That is pretty cool you created a function that worked for the test data!Anschluss
I'm still not sure what NJection does, but if it really decompiles the delegate, then my answer won't help you. Also, for simplifying expression trees you could use relinq's PartialEvaluatingExpressionTreeVisitor. It probably won't give you the a+a+a -> 3*a transformation, but it will certainly convert 2+4+3 to 9.Lagging
They had a site and it was up about a month ago but it looks like their go-daddy domain expired. It is basically a tool that allows expressions to be saved as xml but it has this one function that can turn a func into an expr. I looked at relinq but could not find any code examples for PartialEvaluatingExpressionTreeVisitor. I'll try using it. Thank you for the ideas and all your help. It helped me out.Anschluss
What it does - it just evaluates every subtree that can be evaluated (i.e. doesn't contain parameters). You just call its static EvaluateIndependentSubtrees method with your Expression and it returns the simplified Expression back to you. Btw, you don't have to download whole library - you can get the source of this class from github + a couple of neighboring classes and use them (and eventually - modify towards your needs).Lagging
N
2

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.

Niccolite answered 14/12, 2015 at 22:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.