How can I pass a property as a delegate?

BenAlabaster picture BenAlabaster · Jul 30, 2010 · Viewed 10.5k times · Source

This is a theoretical question, I've already got a solution to my problem that took me down a different path, but I think the question is still potentially interesting.

Can I pass object properties as delegates in the same way I can with methods? For instance:

Let's say I've got a data reader loaded up with data, and each field's value needs to be passed into properties of differing types having been checked for DBNull. If attempting to get a single field, I might write something like:

if(!rdr["field1"].Equals(DBNull.Value)) myClass.Property1 = rdr["field1"];

But if I've got say 100 fields, that becomes unwieldy very quickly. There's a couple of ways that a call to do this might look nice:

myClass.Property = GetDefaultOrValue<string>(rdr["field1"]); //Which incidentally is the route I took

Which might also look nice as an extension method:

myClass.Property = rdr["field1"].GetDefaultOrValue<string>();

Or:

SetPropertyFromDbValue<string>(myClass.Property1, rdr["field1"]); //Which is the one that I'm interested in on this theoretical level

In the second instance, the property would need to be passed as a delegate in order to set it.

So the question is in two parts:

  1. Is this possible?
  2. What would that look like?

[As this is only theoretical, answers in VB or C# are equally acceptable to me]

Edit: There's some slick answers here. Thanks all.

Answer

Kirk Woll picture Kirk Woll · Jul 30, 2010

I like using expression trees to solve this problem. Whenever you have a method where you want to take a "property delegate", use the parameter type Expression<Func<T, TPropertyType>>. For example:

public void SetPropertyFromDbValue<T, TProperty>(
    T obj,
    Expression<Func<T, TProperty>> expression,
    TProperty value
)
{
    MemberExpression member = (MemberExpression)expression.Body;
    PropertyInfo property = (PropertyInfo)member.Member;
    property.SetValue(obj, value, null);
}

Nice thing about this is that the syntax looks the same for gets as well.

public TProperty GetPropertyFromDbValue<T, TProperty>(
    T obj,
    Expression<Func<T, TProperty>> expression
)
{
    MemberExpression member = (MemberExpression)expression.Body;
    PropertyInfo property = (PropertyInfo)member.Member;
    return (TProperty)property.GetValue(obj, null);
}

Or, if you're feeling lazy:

public TProperty GetPropertyFromDbValue<T, TProperty>(
    T obj,
    Expression<Func<T, TProperty>> expression
)
{
    return expression.Compile()(obj);
}

Invocation would look like:

SetPropertyFromDbValue(myClass, o => o.Property1, reader["field1"]);
GetPropertyFromDbValue(myClass, o => o.Property1);