如何使用表达式设置属性值?

78

给定以下方法:

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但私有setter的属性的类:

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

}

有什么建议吗?

你为什么要这样做呢?如果属性有一个私有的setter,那么它就不应该从对象外部被改变!你提出的函数会破坏程序的语义。 - Vladislav Zorov
1
@VladislavZorov 我能预见到这样的评论,我也同意你的观点。在这种情况下,需要在单元测试中准备第三方DTO,这将是最简单的方法。反射也有其用途。 - Anastasiosyal
可能是如何设置属性选择器Expression<Func<T, TResult>>的值的重复问题 https://dev59.com/x2445IYBdhLWcg3wBlyJ - nawfal
1个回答

157
你可以使用扩展方法作弊,让生活变得更轻松:
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

28
Property.SetValue是反射。你不应该使用它。 - MBoros
5
这似乎无法处理嵌套对象,你知道我该如何解决这个问题吗? - Gaz
4
你可以使用泛型类型作为值类型:`public static void SetPropertyValue(this T target, Expression> 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); } } }` - pinus.acer
2
我已经将 @pinus.acer 的评论合并到答案中,因为它对我起作用了。 - anon
4
@Mboros: 使用反射有什么问题吗?如果你只偶尔使用它,可能会慢一些,但如果你进行数百万次操作,那么可能会变得很慢... - VikciaR
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接