views:

273

answers:

4

Hello,

I'm currently working on an application where I need to load data from an SQL database and then assign the retrieved values into the properties of an object. I'm doing this by using reflection since the property names and column names are the same. However, many of the properties are using a custom struct type that is basically a currency wrapper for the decimal type. I have defined an implicit conversion in my struct:

public static implicit operator Currency(decimal d)
{
     return new Currency(d);
}

This works fine when I use it in code. However, when I have this:

foreach (PropertyInfo p in props)
{
     p.SetValue(this, table.Rows[0][p.Name], null);
}

It throws an ArgumentException stating that it cannot convert from System.Decimal to Currency. I'm confused since it works fine in any other circumstance.

A: 

Whilst I am not answering your problem, I think in this kind of situation it would be more appropriate to use a ORM Framework like the Entity Framework or NHibernate which will map your tables into your domain objects and handle all the conversions for you. Using something like reflection to figure out what fields to fill in a domain object is a slow way to do it.

Paul
Normally I'd be pretty worried about it, but it doesn't really impact it from what I can gather. It's a relatively small set of data (56 columns/properties) so the added overhead isn't a big issue. I appreciate the suggestion though.
Chris Boden
+2  A: 

I assume that table is of type DataTable in your code, so the first indexer returns a DataRow, and the second one returns an object. Then PropertyInfo.SetValue also takes an object as the second argument. At no place in this code a cast happens in the code, which is why the overloaded conversion operator is not applied.

Generally speaking, it is only applied when static types are known (forgetting about dynamic in C# 4.0 for the moment). It is not applied when boxing and unboxing things. In this case, the indexer on DataRow boxes the value, and PropertyInfo.SetValue tries to unbox it to a different type - and fails.

Pavel Minaev
+7  A: 

Unfortunately, these user-defined conversion operators are not used by the runtime; they are only used by the compiler at compile time. So if you take a strongly-typed decimal and assign it to a strongly-typed Currency, the compiler will insert a call to your conversion operator and everybody's happy. However, when you call SetValue as you're doing here, the runtime expects you to give it a value of the appropriate type; the runtime has no idea that this conversion operator exists, and won't ever call it.

Charlie
Exactly. Implicit Conversions are similar to extension methods and default parameters - something the compiler does.
TomTom
Ah, I see. I appreciate the insight, I should be able to figure out a better way to do this. Thank you :)
Chris Boden
+2  A: 

I think you need to first unbox the value in table.Rows[0][p.Name] as a decimal.

In other words:

foreach (PropertyInfo p in props)
{
     if (p.PropertyType == typeof(Currency))
     {
         Currency c = (decimal)table.Rows[0][p.Name];
         p.SetValue(this, c, null);
     }
     else
     {
         p.SetValue(this, table.Rows[0][p.Name], null);
     }
}

This is an issue I've seen once or twice before, so I actually decided to write a blog post about it. Anybody looking for a little more explanation, feel free to give it a read.

Dan Tao
This worked excellently! Thank you!
Chris Boden