用表达式树读取对象的属性

时间:2013-05-08 08:48:38

标签: c# .net lambda expression-trees

我想为动态读取值的Object的每个属性创建一个Lambda表达式。

到目前为止我所拥有的:

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

只要“TypeOfProperty”被硬编码,代码就可以调用functionThatGetsValue。我知道我无法动态传递“TypeOfPoperty”。我能做些什么来实现我的目标?

3 个答案:

答案 0 :(得分:8)

假设您对Func<TType, object>代表感到满意(根据上述评论),您可以使用Expression.Convert来实现这一目标:

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

答案 1 :(得分:6)

经过数小时的谷歌搜索后找到答案here。我已经从博客文章中添加了片段,因为它可能会帮助其他人遇到同样的麻烦:

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

答案 2 :(得分:0)

我已经修改了上面的gsharp帖子,直接实际设置了值,使它更容易使用。由于DynamicCast功能的引入要求您事先了解您的类型,因此它并不理想。我的目标是尽量保持强类型,不返回对象,避免使用动态关键字。此外,将“魔法”保持在最低限度。

  public static T DynamicCast<T>(this object value)
    {
        return (T) value;
    }
    public static object GetPropertyValue<T>(this PropertyInfo propertyInfo, T objectInstance)
    {
        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, propertyInfo.PropertyType);
        var lambda =  Expression.Lambda(convert, instance).Compile();
        var result = lambda.DynamicInvoke(objectInstance);


        return result;
    }

    public static void SetPropertyValue<T, TP>(this PropertyInfo propertyInfo, T objectInstance, TP value)
        where T : class
        where TP : class
    {
        if (typeof(T) != propertyInfo.DeclaringType)
        {
            throw new ArgumentException();
        }

        var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
        var argument = Expression.Parameter(propertyInfo.PropertyType, "a");
        var setterCall = Expression.Call(
            instance,
            propertyInfo.GetSetMethod(),
            Expression.Convert(argument, propertyInfo.PropertyType));

        var lambda = Expression.Lambda(setterCall, instance, argument).Compile();
        lambda.DynamicInvoke(objectInstance, value);
    }

示例:

        public void Get_Value_Of_Property()
    {
        var testObject = new ReflectedType
        {
            AReferenceType_No_Attributes = new object(),
            Int32WithRange1_10 = 5,
            String_Requires = "Test String"
        };

        var result = testObject.GetType().GetProperty("String_Requires").GetPropertyValue(testObject).DynamicCast<string>();

        result.Should().Be(testObject.String_Requires);
    }

  public void Set_Value_Of_Property()
        {
            var testObject = new ReflectedType
            {
                AReferenceType_No_Attributes = new object(),
                Int32WithRange1_10 = 5,
                String_Requires = "Test String"
            };

            testObject.GetType().GetProperty("String_Requires").SetPropertyValue(testObject, "MAGIC");

            testObject.String_Requires.Should().Be("MAGIC");
        }

可以编写一个帮助方法,该方法使用MakeGenericMethod或表达式树使lambda根据PropertyInfo对象进行类型调用以调用DynamicCast,并避免必须事先知道它。但那不太优雅。