Reading Properties of an Object with Expression Trees

12,315

Solution 1

Assuming that you're happy with a Func<TType, object> delegate (as per the comments above), you can use Expression.Convert to achieve that:

var properties = typeof(TType).GetProperties().Where(p => p.CanRead);

foreach (var propertyInfo in properties)
{
    MethodInfo getterMethodInfo = propertyInfo.GetGetMethod();
    ParameterExpression entity = Expression.Parameter(typeof(TType));
    MethodCallExpression getterCall = Expression.Call(entity, getterMethodInfo);

    UnaryExpression castToObject = Expression.Convert(getterCall, typeof(object));
    LambdaExpression lambda = Expression.Lambda(castToObject, entity);

    var functionThatGetsValue = (Func<TType, object>)lambda.Compile();
}

Solution 2

After hours of googling found the answer here. I've added the snippets from the blog post as it might help others having the same troubles:

public static class PropertyInfoExtensions
{
    public static Func<T, object> GetValueGetter<T>(this PropertyInfo propertyInfo)
    {
        if (typeof(T) != propertyInfo.DeclaringType)
        {
            throw new ArgumentException();
        }

        var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
        var property = Expression.Property(instance, propertyInfo);
        var convert = Expression.TypeAs(property, typeof(object));
        return (Func<T, object>)Expression.Lambda(convert, instance).Compile();
    }

    public static Action<T, object> GetValueSetter<T>(this PropertyInfo propertyInfo)
    {
        if (typeof(T) != propertyInfo.DeclaringType)
        {
            throw new ArgumentException();
        }

        var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
        var argument = Expression.Parameter(typeof(object), "a");
        var setterCall = Expression.Call(
            instance,
            propertyInfo.GetSetMethod(),
            Expression.Convert(argument, propertyInfo.PropertyType));
        return (Action<T, object>)Expression.Lambda(setterCall, instance, argument).Compile();
    }
}
Share:
12,315
gsharp
Author by

gsharp

If you like StackOverflow favorites and wish improvements read my meta post and vote up.

Updated on July 18, 2022

Comments

  • gsharp
    gsharp almost 2 years

    I want to create a Lambda Expression for every Property of an Object that reads the value dynamically.

    What I have so far:

    var properties = typeof (TType).GetProperties().Where(p => p.CanRead);
    
    foreach (var propertyInfo in properties)
    {
        var getterMethodInfo = propertyInfo.GetGetMethod();
    
        var entity = Expression.Parameter(typeof (TType));
    
        var getterCall = Expression.Call(entity, getterMethodInfo);
    
        var lambda = Expression.Lambda(getterCall, entity);
        var expression = (Expression<Func<TType, "TypeOfProperty">>) lambda;
        var functionThatGetsValue = expression.Compile();
    }
    

    The code Works well when i call functionThatGetsValue as long as "TypeOfProperty" is hardcoded. I know that I can't pass the "TypeOfPoperty" dynamically. What can I do to achive my goal?

    • LukeH
      LukeH almost 11 years
      What is your goal? You say you want to create a lambda expression; do you only need the compiled delegate (functionThatGetsValue), or do you need the intermediate expression tree too (expression)?
    • gsharp
      gsharp almost 11 years
      @LukeH, just the compiled delegate. thanks. (My goal is to iterate through a list of objects, and read all the values from the properties. To gain a bit of performance I want to do it this way instead of using reflection)
    • Oleh Nechytailo
      Oleh Nechytailo almost 11 years
      When I tried to achieve similar result I ended with returning Func<Type, Object> and casting return value to specific property type on caller`s side.
    • LukeH
      LukeH almost 11 years
      I agree with Nechytailo that you'll probably need to create your delegate as a Func<TType,object>, invoke it, and then cast the result to its specific type (if necessary).
    • gsharp
      gsharp almost 11 years
      I would be fine with that, but the problem is that from "getterMethodInfo" it takes the actualy type and when I try to cast the lambda to Func<TType,object> it can't be casted.