属性选择器Expression <func <t>&gt;。如何获取/设置选定属性的值</func <t>

时间:2011-02-22 07:55:13

标签: c#

我有一个我希望以这种方式构建的对象:

var foo = new FancyObject(customer, c=>c.Email); //customer has Email property

我应该如何声明第二个参数?

访问所选属性setter / getter的代码如何?

UPD。模型中有多个实体具有Email属性。签名可能看起来像是:

public FancyObject(Entity holder, Expression<Func<T>> selector)

和构造函数调用

var foo = new FancyObject(customer, ()=>customer.Email);

2 个答案:

答案 0 :(得分:101)

参数为Expression<Func<Customer,string>> selector。阅读它可以通过平面编译:

 Func<Customer,string> func = selector.Compile();

然后您可以访问func(customer)。分配比较棘手;对于简单的选择器,你可以希望你可以简单地分解为:

var prop = (PropertyInfo)((MemberExpression)selector.Body).Member;
prop.SetValue(customer, newValue, null);

但更复杂的表达式要么需要手动树遍,要么需要4.0表达式节点类型中的一些:

        Expression<Func<Customer, string>> email
             = cust => cust.Email;

        var newValue = Expression.Parameter(email.Body.Type);
        var assign = Expression.Lambda<Action<Customer, string>>(
            Expression.Assign(email.Body, newValue),
            email.Parameters[0], newValue);

        var getter = email.Compile();
        var setter = assign.Compile();

答案 1 :(得分:6)

看起来类型必须是通用的两个类型参数 - 源和结果。例如,您可以使用:

var foo = new FancyObject<Customer, string>(customer, c => c.Email);

第一个参数的类型为TSource,第二个参数为Expression<Func<TSource, TResult>>

public class FancyObject<TSource, TResult>
{
    private readonly TSource value;
    private readonly Expression<Func<TSource, TResult>> projection;

    public FancyObject(TSource value, 
                       Expression<Func<TSource, TResult>> projection)
    {
        this.value = value;
        this.projection = projection;
    }
}

您可以使用非泛型类型的静态方法更简单:

var foo = FancyObject.Create(customer, c => c.Email);

可以使用类型推断来计算类型参数。