Expression Tree using Action possible?

c# expression-trees generics reflection

Question

I am having a little trouble with something I am working on. I initially created a generic layer that sits between my business objects and the data access layer which is working fine. I then recently read about something called Expression Trees which is apparently more efficient and has proven to be so as I swapped Activator.CreateInstance() with an expression and has improved my generic layer exponentially.

I am still doing some reading about the whole area (Expressions) but I came across some code which I want to try making generic. At the moment, you have to pass in a concrete type such as a string, int, decimal etc. I was this bit to be generic. I Tried a couple of things but failed. The bit I want generic is Action, I don't want to pass in a string I want to be able to pass in the type of the property generically, i.e. typeof(T).GetProperty("Forename").PropertyType. Is this possible? Was thinking of doing a switch statement which is kinda foo bar.

Thanks in advance, Onam.

public class TTT<T> where T : new()
{
    public void Do(object t)
    {
        MethodInfo info = typeof(T).GetProperty("Forename").GetSetMethod();

        ParameterExpression param = Expression.Parameter(typeof(string), "val");

        MethodCallExpression call = Expression.Call(Expression.Constant(t), info,
            new ParameterExpression[] { param });

        Action<string> action = Expression.Lambda<Action<string>>(call, param).Compile();

        action("hi");
    }
}

Accepted Answer

First, note that this is not a good way to do this; there is no performance advantage if you are building an Expression per-call, and then Compile()-ing it, and then invoking it. Reflection would be faster. If you need performance, look at a library such as "FastMember", where this would just be:

var accessor = TypeAccessor.Create(typeof(T));
accessor[target, "Forename"] = value;

(where that is fully optimized via meta-programming and automatic caching)


If you want the type to be dynamic, then there are two options:

  • type it using Expression.GetActionType and use DynamicInvoke - really bad performance (hint: don't do this)
  • type it as Action<object> and do a cast inside the expression (fine)

So something like:

using System;
using System.Linq.Expressions;
using System.Reflection;
class Foo
{
    public string Forename {get;set;}
}
class Test<T>
{
    public void Do(object target, object value)
    {
        var obj = Expression.Constant(target, typeof(T));
        var member = Expression.PropertyOrField(obj, "Forename");
        var param = Expression.Parameter(typeof(object));
        Type type;
        switch(member.Member.MemberType)
        {
            case MemberTypes.Field:
                type = ((FieldInfo)member.Member).FieldType; break;
            case MemberTypes.Property:
                type = ((PropertyInfo)member.Member).PropertyType; break;
            default:
                throw new NotSupportedException(member.Member.MemberType.ToString());
        }
        var body = Expression.Assign(member, Expression.Convert(param, type));
        var lambda = Expression.Lambda<Action<object>>(body, param);
        lambda.Compile()(value);
    }
}
static class Program
{
    static void Main()
    {
        var obj = new Foo();
        new Test<Foo>().Do(obj, "abc");
        Console.WriteLine(obj.Forename);
    }
}


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow