views:

71

answers:

2

I want to compare two values at runtime using reflection. I was using Comparer.Default.Compare(x,y) for this, but I have come to realize that this is not adequate. Let's say I want to compare a double to a single (1.0 == 10). Comparer.Default will throw an exception because it insists that both values must be the same type (double). However, an explicit conversion exists for this, which is really what I want to use.

So, why can't I just use Convert.ChangeType? Take the case of 1.25 > 1 (double > integer). If I try Convert.ChangeType(1.25,typeof(int)) on 1.25, I will get 1, and the assertion above will fail, when really 1.25 IS > 1.

So, can someone please suggest a way of invoking the explicit comparison (if it exists) that a type defines?

Thanks.

+3  A: 

Are you using C# 4 and .NET 4? If so, it's really easy using dynamic typing:

dynamic x = firstValue;
dynamic y = secondValue;
if (x > y) // Or whatever

The compiler performs all the appropriate conversions for you.

Jon Skeet
Nice option! I always forget about using dynamic for this.
Reed Copsey
Can I do this with expression trees? (.NET 2.0 + DLR is an option for me, but 4.0 is not) I tried emitting a fast invoker that performed basically lamda'ing the comparison Expression.Convert(Expression.Parameter(typeof(single)),typeof(double)) on my single value, but that failed with cast is invalid at execution time (not compile time). I could have done something wrong, as I only spent about 5 minutes trying this then had to run out to lunch...
JeffN825
@JeffN825: I think you'll need to work out the right conversions for yourself. It won't be a lot of fun.
Jon Skeet
Your post got me thinking, I ended up just dumping my comparison code in a VB .NET project targeting 2.0 with late binding enabled, then referencing that from my other project and ilmerging it in as a post build step. This seems to do the trick. I will probably reflector the vb code tomorrow and try emitting that into a DynamicMethod instead.
JeffN825
+1  A: 

If C# 4 is an option, Jon Skeet's suggestion of using dynamic is most likely ideal.

If it is not, then...

There isn't an explicit comparison. The compiler, at compile time, does the conversion, then invokes the appropriate comparison.

Your best bet is to use Convert.ChangeType to convert to the wider type, and then do the comparison on the result. If you don't want to handle checking of all types, you can typically convert both sides to decimal values, then use a single comparison to check them, as decimal should handle all of your values adequately.

Reed Copsey
thanks...but how do I determine the "wider" type?
JeffN825
@JeffN825: You'd have to manually check it - which is why I had suggested just using decimal (since it's always as wide/wider).
Reed Copsey
What about the case where double has a higher max value/lower min value than decimal?
JeffN825
but double has a higher min and max value than decimal...you can't cast (decimal)double.MaxValue. Or am I misunderstanding?
JeffN825
@Reed: Decimal has a *much* smaller range than double. While you won't lose precision in terms of significant digits, there are plenty of doubles which aren't even slightly representable as decimal.
Jon Skeet
Its only a problem if you're going above about +/- 8e+29 - but then it's a problem. Depends on what you're doing -
Reed Copsey