2012-03-07 50 views
45

考虑以下方法:如何使用表达式设置属性值?

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

你会如何去写它的表达能等同,而不需要为目标的额外的参数传递?

为什么不直接设置属性我可以听到你说。例如,假设我们有以下的类有一个公共的getter但私人二传手属性:

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

我想能够调用:

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

现在这里是一些示例代码。

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 

} 

任何指针?

+0

你为什么要这么做?如果该物业拥有私人二传手,那么它并不意味着从物体外面改变!您提议的功能会破坏程序的语义。 – 2012-03-07 12:56:41

+1

@VladislavZorov我可以看到这样的评论,我分享你的看法。在这种情况下,第三方DTO需要在单元测试中启动,这将是最简单的方法。反思也有其用处。 – Anastasiosyal 2012-03-07 13:03:18

+0

可能的重复[如何设置值属性选择器表达式>](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 2013-04-17 20:43:32

回答

91

你可以欺骗,让生活更容易扩展方法:

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

然后:

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

之所以这样,是比较容易的,因为你已经在目标上延方法被调用。此外,lambda表达式是一个简单的成员表达式,没有闭包。在你原来的例子中,目标在封闭中被捕获,并且到底层目标和PropertyInfo可能有点棘手。

+0

+1谢谢你提到关闭,想知道FieldExpression在那里做什么。这意味着可以通过完全不优雅的方式来达到该值:'((memberSelectorExpression.Expression as MemberExpression).Expression as ConstantExpression).Value'为了获得封闭字段的值。我喜欢你的方法,增加一个通用的扩展方法。 – Anastasiosyal 2012-03-07 13:12:50

+1

这里似乎有些问题。该属性不会更改,并且直接投射会引发异常:'无法投射'System.Linq.Expressions.UnaryExpression'类型的对象来键入'System.Linq.Expressions.MemberExpression'。 – Stijn 2013-02-11 07:40:21

+14

Property.SetValue正在反射。你不应该使用它。 – MBoros 2014-05-30 12:26:44