10

我想为动态读取值的对象的每个属性创建一个 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();
}

functionThatGetsValue只要“TypeOfProperty”是硬编码的,当我打电话时,代码就可以很好地工作。我知道我不能动态传递“TypeOfPperty”。我能做些什么来实现我的目标?

4

3 回答 3

9

假设您对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();
}
于 2013-05-08T12:02:33.620 回答
7

经过数小时的谷歌搜索,在这里找到了答案。我添加了博客文章中的片段,因为它可能会帮助其他遇到同样问题的人:

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();
    }
}
于 2013-05-08T12:08:38.473 回答
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 并避免事先知道它。但这不那么优雅。

于 2017-06-23T06:08:44.433 回答