Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I pass a property as a delegate?

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.

like image 956
BenAlabaster Avatar asked Jul 30 '10 13:07

BenAlabaster


People also ask

How do you use delegates?

Delegates allow methods to be passed as parameters. Delegates can be used to define callback methods. Delegates can be chained together; for example, multiple methods can be called on a single event. Methods don't have to match the delegate type exactly.

What is a delegate method?

A delegate method is a method that the delegate object is expected to implement. Some delegate methods are required, while some are not. In IOS, most delegates are expected to conform to an Objective-C protocol; the protocol declaration will tell you which methods are optional and which are required.

Can delegates be private?

Just like classes and interfaces, we can declare delegates outside of classes or nested within classes. We can mark them private , public , or internal .

What is the necessary condition for implementing delegates?

2. To implement delegates, the necessary condition is? Explanation: None.


2 Answers

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);
like image 109
Kirk Woll Avatar answered Oct 07 '22 20:10

Kirk Woll


(Adding a second answer because it's on a completely different approach)

To address your original problem, which is more about wanting a nice API for mapping named values in a datareader to properties on your object, consider System.ComponentModel.TypeDescriptor - an often overlooked alternative to doing reflective dirtywork yourself.

Here's a useful snippet:

var properties = TypeDescriptor.GetProperties(myObject)
    .Cast<PropertyDescriptor>()
    .ToDictionary(pr => pr.Name);

That creates a dictionary of the propertydescriptors of your object.

Now I can do this:

properties["Property1"].SetValue(myObject, rdr["item1"]);

PropertyDescriptor's SetValue method (unlike System.Reflection.PropertyInfo's equivalent) will do type conversion for you - parse strings as ints, and so on.

What's useful about this is one can imagine an attribute-driven approach to iterating through that properties collection (PropertyDescriptor has an Attributes property to allow you to get any custom attributes that were added to the property) figuring out which value in the datareader to use; or having a method that receives a dictionary of propertyname - columnname mappings which iterates through and performs all those sets for you.

I suspect an approach like this may give you the API shortcut you need in a way that lambda-expression reflective trickery - in this case - won't.

like image 39
James Hart Avatar answered Oct 07 '22 20:10

James Hart