C# 4 “dynamic” in expression trees

2019-01-21 00:42发布

问题:

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)?

回答1:

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();


回答2:

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;
    }
}


回答3:

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