Селектор свойств Expression <Func <T>>. Как получить/установить значение для выбранного свойства
У меня есть объект, который я хочу построить таким образом:
var foo = new FancyObject(customer, c=>c.Email); //customer has Email property
Как объявить второй параметр?
Как будет выглядеть код, который будет обращаться к выбранному устройству/получателю свойств?
Upd. В модели есть несколько объектов, у которых есть свойство Email. Вероятно, подпись будет выглядеть так:
public FancyObject(Entity holder, Expression<Func<T>> selector)
и вызов конструктора
var foo = new FancyObject(customer, ()=>customer.Email);
Ответы
Ответ 1
Параметр будет 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 node -types:
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();
Ответ 2
Похоже, что тип должен быть общим с двумя параметрами типа - источником и результатом. Например, вы можете использовать:
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);
Это может использовать вывод типа для определения аргументов типа.