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:
[As this is only theoretical, answers in VB or C# are equally acceptable to me]
Edit: There's some slick answers here. Thanks all.
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.
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.
Just like classes and interfaces, we can declare delegates outside of classes or nested within classes. We can mark them private , public , or internal .
2. To implement delegates, the necessary condition is? Explanation: None.
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);
(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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With