DerApe
DerApe

Reputation: 3175

Convert generic getter expression to object getter expression

I have a expression getter like that:

var expression = () => SomeInstance.Nr;

It is passed into a method:

public void AddExpression<T>(Expression<Func<T>> func)

Now I would like to convert that generic expression to

Expression<Func<object>>

I'm not quite sure if I can even do that. I tried something like this:

var converted = Expression.Convert(func, typeof(object));
var objectExpression = Expression.Lambda<Func<object>>(Expression.Call(converted.Method), func.Parameters);

But when I call

var number = objectExpression.Compile()();

It won't return the property value.

The code can be tested here: http://volatileread.com/utilitylibrary/snippetcompiler?id=25062

Update: It seems that the invoke is wrapped a second time:

var converted = Expression.Convert(func, typeof(object));
var objectExpression = Expression.Lambda<Func<object>>(Expression.Call(converted.Method), func.Parameters);
var anotherDelegate = objectExpression.Compile().Invoke(); // would have expected the value here
var value = ((Delegate)anotherDelegate).DynamicInvoke(); // this now does return the value

Upvotes: 1

Views: 792

Answers (1)

Luaan
Luaan

Reputation: 63772

Don't use Expression.Call on delegates - that's only there to call methods. Instead, you want to use Expression.Invoke, which is specifically there to invoke delegates. Also, the key to doing expression trees is wrapping - invoke the inner delegate, convert the result, and wrap this in a lambda:

Expression<Func<int>> inputExpression = () => 42;

var newLambda = 
    Expression.Lambda<Func<object>>
    (
        Expression.Convert
        (
            Expression.Invoke(inputExpression), 
            typeof(object)
        )
    );

Console.WriteLine(newLambda.Compile()()); // Prints 42.

newLambda is Expression<Func<object>> and invoking it gives you 42, as you'd expect.

And of course, this makes it rather easy to make this an extension method (although you probably want to use templates to generate all the different Func<...> overloads if you need them).

Do note that this is only going to work if whatever LINQ provider you're using actually supports Invoke - in this case, it's not a problem because the lambda compiler can handle it, but if you need to use something like this with e.g. EntityFramework, you'll need to take a slightly different approach - you'll need to unwrap the inner lambda, convert the body of the inner lambda, and then wrap it again in another lambda. For a parameter-less expression, this is rather easy:

Expression<Func<int>> inputExpression = () => 42;

var newLambda = 
  Expression.Lambda<Func<object>>
  (
    Expression.Convert(inputExpression.Body, typeof(object))
  );

Console.WriteLine(newLambda.Compile()()); // Prints 42.

Also, for completeness, note that this only works if the inner expression is really a constant expression, and not a quote - but if you needed quoted nested expressions, you probably wouldn't be asking this question :D

Upvotes: 3

Related Questions