una sirven significa que propiedades propiedad programacion para orientada objetos informatica entre diferencia declaracion clase campo autoimplementadas agregar c# .net lambda expression-trees

c# - sirven - Lectura de propiedades de un objeto con árboles de expresión



propiedades en programacion orientada a objetos (3)

Quiero crear una expresión Lambda para cada propiedad de un objeto que lea el valor de forma dinámica.

Lo que tengo hasta ahora

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

El código funciona bien cuando llamo a functionThatGetsValue siempre que "TypeOfProperty" esté codificado. Sé que no puedo pasar el "TypeOfPoperty" dinámicamente. ¿Qué puedo hacer para lograr mi objetivo?


Después de horas de google encontró la respuesta aquí . Agregué los fragmentos de la publicación del blog, ya que podría ayudar a otros a tener los mismos problemas:

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


Suponiendo que esté satisfecho con un delegado Func<TType, object> (según los comentarios anteriores), puede usar Expression.Convert para lograr eso:

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 castToObject = Expression.Convert(getterCall, typeof(object)); var lambda = Expression.Lambda(castToObject, entity); var functionThatGetsValue = (Func<TType, object>)lambda.Compile(); }


Modifiqué la publicación de gsharp anterior para establecer realmente el valor directamente y hacerlo un poco más fácil de usar. No es ideal, ya que es la introducción de la función DynamicCast que requiere que conozca su tipo por adelantado. Mi objetivo era tratar de mantenernos fuertemente tipados y no devolver objetos y evitar palabras clave dinámicas. Además, mantenga la "magia" al mínimo.

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

Ejemplos:

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

Puede escribir un método auxiliar que use MakeGenericMethod o un árbol de expresiones para hacer que una lambda haga la llamada tipeada para llamar a DynamicCast en función del objeto PropertyInfo y evite tener que conocerlo por adelantado. Pero eso es menos elegante.