Reputation: 3175
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
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