简体   繁体   中英

Expression Trees with dynamic parameter

I want to convert this:

Func<dynamic, object> myFunc = t => return t.Name + " " + t.Surname;

Into an Expression Tree.

What I have came up with, is this:

ParameterExpression target = ExpressionParameter(typeof(dynamic), "target");
ParameterExpression result = ExpressionParameter(typeof(object), "result");
BlockExpression block = Expression.Block(
     new [] { result },
     Expression.Assign(
           result,
           Expression.Add(
                 Expression.Add(
                      Expression.Property(target, "Name"),
                      Expression.Constant(" ", typeof(string))
                 ),
                 Expression.Property(target, "Surname")
           )
     )
);
Func<dynamic, object> myFunc = Expression.Lambda<dynamic, object>>(block, target).Compile();

However, the compiler doesn't like typeof(dynamic) , and I kind of get it. dynamic isn't a type, it is an object in essence.

So I proceeded to change the ParameterExpression :

ParameterExpression target = ExpressionParameter(typeof(object), "target");

The code now compiles, but there is a problem at runtime.

I am trying to get the value of the property Name of target , which may have sense if the object was dynamic .

But since target is considered of type object , the Expression throws an error telling me Name doesn't exist as a property.

Is there an Expression for fetching a dynamic property?

For those who are or were interested in a solution:

ParameterExpression target = Expression.Parameter(typeof(object), "target");
ParameterExpression result = Expression.Parameter(typeof(object), "result");

CallSiteBinder getName = Binder.GetMember(
   CSharpBinderFlags.None, "Name", typeof(Program),
   new CSharpArgumentInfo[] {
       CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
   }
);

CallSiteBinder getSurname= Binder.GetMember(
   CSharpBinderFlags.None, "Surname", typeof(Program),
   new CSharpArgumentInfo[] {
       CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
   }
);

BlockExpression block = Expression.Block(
    new[] { result },
    Expression.Assign(
        result,
        Expression.Call(typeof(string).GetMethod("Concat", new Type[] { typeof(object[]) }),
                        Expression.NewArrayInit(typeof(object),
                             Expression.Dynamic(getName, typeof(object), target),
                             Expression.Constant(" ", typeof(object)),
                             Expression.Dynamic(getSurname, typeof(object), target)
                        )
       )
    )
);

Func<dynamic, object> myFunc = Expression.Lambda<Func<dynamic, object>>(block, target).Compile();

Here's exactly what I am doing:

  1. Created a CallSiteBinder that obtains the value of the dynamic property Name of the dynamic object passed as argument
  2. Created a CallSiteBinder that obtains the value of the dynamic property Surname of the dynamic object passed as argument
  3. Invoked the method string.Concat(params object[] args) . For that, I need to send my arguments as an array of object . I'm creating the array with the values of getName , " " , and getSurname .

I used the following answer as a guide and reference:

C# 4 “dynamic” in expression trees

With the above approach, one could do something like this:

dynamic person = new ExpandoObject();
person.Name = "Matt";
person.Surname = "Smith";

object value = myFunc(person);
Console.WriteLine(value); //Will print out "Matt Smith"

//Internally it just calls:
//string.Concat(new object[] { person.Name, " ", person.Surname });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM