2012-03-07 15 views
45

Dado el siguiente método:¿Cómo establecer el valor de propiedad usando Expresiones?

public static void SetPropertyValue(object target, string propName, object value) 
{ 
    var propInfo = target.GetType().GetProperty(propName, 
         BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); 

    if (propInfo == null) 
     throw new ArgumentOutOfRangeException("propName", "Property not found on target"); 
    else 
     propInfo.SetValue(target, value, null); 
} 

Cómo haría usted para escribir está habilitado expresión equivalente, sin necesidad de pasar de un parámetro adicional para el objetivo?

¿Por qué hacer esto en vez de establecer la propiedad directamente? Puedo oírte decir. Por ejemplo supongamos que tenemos la siguiente clase con una propiedad que tiene un captador público, pero colocador privada:

public class Customer 
{ 
    public string Title {get; private set;} 
    public string Name {get; set;} 
} 

Me gustaría ser capaz de llamar a:

var myCustomerInstance = new Customer(); 
SetPropertyValue<Customer>(cust => myCustomerInstance.Title, "Mr"); 

Ahora aquí es un código de ejemplo.

public static void SetPropertyValue<T>(Expression<Func<T, Object>> memberLamda , object value) 
{ 
    MemberExpression memberSelectorExpression; 
    var selectorExpression = memberLamda.Body; 
    var castExpression = selectorExpression as UnaryExpression; 

    if (castExpression != null) 
     memberSelectorExpression = castExpression.Operand as MemberExpression; 
    else 
     memberSelectorExpression = memberLamda.Body as MemberExpression; 

    // How do I get the value of myCustomerInstance so that I can invoke SetValue passing it in as a param? Is it possible 

} 

¿Alguna sugerencia?

+0

¿Por qué querrías hacer eso? Si la propiedad tiene un setter privado, ¡no debe cambiarse desde fuera del objeto! La función que está proponiendo rompe la semántica de su programa. –

+1

@VladislavZorov Pude ver un comentario así y comparto tu opinión. En este caso, un DTO de un tercero debe prepararse en una prueba unitaria y este sería el enfoque más simple para hacerlo. La reflexión también tiene sus usos. – Anastasiosyal

+0

posible duplicado de [Cómo establecer el valor de un selector de propiedad Expresión >] (http://stackoverflow.com/questions/8107134/how-set-value-a-property-selector-expressionfunct-tresult) http: // stackoverflow.com/questions/5075484/property-selector-expressionfunct-how-to-get-set-value-to-selected-property – nawfal

Respuesta

91

Se puede engañar y hacer la vida más fácil con un método de extensión:

public static class LambdaExtensions 
{ 
    public static void SetPropertyValue<T, TValue>(this T target, Expression<Func<T, TValue>> memberLamda, TValue value) 
    { 
     var memberSelectorExpression = memberLamda.Body as MemberExpression; 
     if (memberSelectorExpression != null) 
     { 
      var property = memberSelectorExpression.Member as PropertyInfo; 
      if (property != null) 
      { 
       property.SetValue(target, value, null); 
      } 
     } 
    } 
} 

y luego:

var myCustomerInstance = new Customer(); 
myCustomerInstance.SetPropertyValue(c => c.Title, "Mr"); 

La razón por la que esto es más fácil es porque ya tiene el destino en el que la extensión método es invocado Además, la expresión lambda es una expresión de miembro simple sin cierres. En su ejemplo original, el objetivo se captura en un cierre y podría ser un poco complicado llegar al objetivo subyacente y PropertyInfo.

+0

+1 Gracias por mencionar el cierre, me preguntaba qué hacía FieldExpression allí. Esto significaría que el valor podría ser alcanzado por el completamente no elegante: '((memberSelectorExpression.Expression como MemberExpression) .Expression como ConstantExpression) .Value' para obtener el valor del campo cerrado. Me gusta su enfoque agregando un método de extensión genérico. – Anastasiosyal

+1

Parece que hay algo mal aquí. La propiedad no cambia, y un lanzamiento directo arroja una excepción: 'No se puede lanzar el objeto de tipo 'System.Linq.Expressions.UnaryExpression' para escribir 'System.Linq.Expressions.MemberExpression'. – Stijn

+14

Property.SetValue es reflejo . No deberías usar eso. – MBoros

Cuestiones relacionadas