I'm trying to figure out how to put all the pieces together, and would appreciate a concrete source code sample for a simple case to start with.
Consider the following C# code:
Func<int, int, int> f = (x, y) => x + y;
I can produce an equivalent function at runtime using expression trees as follows:
var x = Expression.Parameter(typeof(int), "x");
var y = Expression.Parameter(typeof(int), "y");
Func<int, int, int> f =
Expression.Lambda<Func<int, int, int>>(
Expression.Add(x, y),
new[] { x, y }
).Compile();
Now given the following lambda:
Func<dynamic, dynamic, dynamic> f = (x, y) => x + y;
how would I generate the equivalent using expression trees (and, presumably, Expression.Dynamic
)?
You can create an expression tree that represents a dynamic C# addition expression by passing the CallSiteBinder for a dynamic C# addition expression into Expression.Dynamic. You can discover the code to create the Binder by running Reflector on the original dynamic expression. Your example would go something like this:
var x = Expression.Parameter(typeof(object), "x");
var y = Expression.Parameter(typeof(object), "y");
var binder = Binder.BinaryOperation(
CSharpBinderFlags.None, ExpressionType.Add, typeof(Program),
new CSharpArgumentInfo[] {
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)});
Func<dynamic, dynamic, dynamic> f =
Expression.Lambda<Func<object, object, object>>(
Expression.Dynamic(binder, typeof(object), x, y),
new[] { x, y }
).Compile();
You cannot do that because an expression tree "May not contain a dynamic operation".
The following will not compile, because of the + operation, for example, and you are trying to build an expression tree that violates that rule:
Expression<Func<dynamic, dynamic, dynamic>> f = (x, y) => x + y;
If you were not doing an Add operation you could get away with it.
See How to create an Expression<Func<dynamic, dynamic>> - Or is it a bug? for more information.
Edit:
This is as close as I can get, by defining my own Add method that takes dynamic parameters and returns a dynamic result.
class Program
{
static void Main(string[] args)
{
var x = Expression.Parameter(typeof(object), "x");
var y = Expression.Parameter(typeof(object), "y");
Func<dynamic, dynamic, dynamic> f =
Expression.Lambda<Func<dynamic, dynamic, dynamic>>(
Expression.Call(typeof(Program), "Add", null, x, y),
new[] { x, y }
).Compile();
Console.WriteLine(f(5, 2));
Console.ReadKey();
}
public static dynamic Add(dynamic x, dynamic y)
{
return x + y;
}
}
Very interesting. I guess it's impossible for the same reason the following does not compile:
Expression<Func<dynamic, dynamic, int>> func = (p1, p2) => p1 + p2;
It's a compiler error CS1963 (which doesn't seem to be documented by MS):
error CS1963: An expression tree may not contain a dynamic operation