Property selector Expression<Func<T>>. How to get/set value to selected property
I have an object that I want to be constructed in such manner:
var foo = new FancyObject(customer, c=>c.Email); //customer has Email property
How should I declare second parameter?
How the code that will access selected property setter/getter will look like?
Upd. There are several entities in the model that has Email property. So probably the signature will looks like:
public FancyObject(Entity holder, Expression<Func<T>> selector)
and the constructor call
var foo = new FancyObject(customer, ()=>customer.Email);
The parameter would be an Expression<Func<Customer,string>> selector
. Reading it can be via flat compile:
Func<Customer,string> func = selector.Compile();
then you can access func(customer)
. Assigning is trickier; for simple selectors your could hope that you can simply decompose to:
var prop = (PropertyInfo)((MemberExpression)selector.Body).Member;
prop.SetValue(customer, newValue, null);
But more complex expressions would either need a manual tree walk, or some of the 4.0 expression 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();
It looks like the type would have to be generic with two type parameters - the source and result. For example, you might use:
var foo = new FancyObject<Customer, string>(customer, c => c.Email);
The first parameter would be of type TSource
, and the second would be 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;
}
}
You can make this simpler to use with a static method in a non-generic type:
var foo = FancyObject.Create(customer, c => c.Email);
That can use type inference to work out the type arguments.