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

我想为动态读取值的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>) lambda; var functionThatGetsValue = expression.Compile(); } 

只要“TypeOfProperty”是硬编码的,当我调用functionThatGetsValue时,代码运行良好。 我知道我无法动态传递“TypeOfPoperty”。 我能做些什么来实现我的目标?

假设您对Func委托感到满意(根据上面的注释),您可以使用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)lambda.Compile(); } 

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

 public static class PropertyInfoExtensions { public static Func GetValueGetter(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)Expression.Lambda(convert, instance).Compile(); } public static Action GetValueSetter(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)Expression.Lambda(setterCall, instance, argument).Compile(); } } 

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

  public static T DynamicCast(this object value) { return (T) value; } public static object GetPropertyValue(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(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(); 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,并避免必须事先知道它。 但那不那么优雅。