3
\$\begingroup\$

I have written a helper method which computes the sum of values in some custom grid, given the column indexes. The method appears to work (for a decimal - as Anthony pointed out, I need to test this further), but I am not at ease with the following conversions:

decimal valIfNull = (decimal)(object)valueIfNull;
return (T)(object)sum;

They seem too indirect, but Convert.ChangeType also returns an object type, so another cast is needed anyway. Also, I believe that Convert.ChangeType can convert a string such as "123.456" into a proper decimal value, but this is not the behavior I want in this case. I would like to see an invalid cast exception in that case. So, is what I have good enough, or is there a better way?

    private T SumColumns<T>(T valueIfNull, params short[] columnIndexes)
        where T : struct, IConvertible
    {
        // Since we do not know what exactly T is, we will compute things as if it was a decimal.
        // If this works reasonably well, then this trick should be used elsewhere.

        // I am not sure if this is the best way to go about casting
        decimal valIfNull = (decimal)(object)valueIfNull;

        // Here this.GetCurrentRowValue<decimal> is another generic function I wrote ...
        decimal sum = columnIndexes.Select(columnIndex => this.GetCurrentRowValue<decimal>(columnIndex: columnIndex, valueIfNull: valIfNull)).Sum();
        return (T)(object)sum;
    }
\$\endgroup\$
  • 2
    \$\begingroup\$ Your method is not going to work if T is not decimal. When you box a value type, you can only unbox to the same type (or its nullable counterpart). For example, given long L = long.MaxValue;, you could not have (decimal)(object)L; \$\endgroup\$ – Anthony Pegram Apr 25 '12 at 1:27
4
\$\begingroup\$

You've already placed a constraint on the first parameter that it should be IConvertible, so the methods of that interface is available to you.

decimal decimalValueIfNull = valueIfNull.ToDecimal(CultureInfo.InvariantCulture);

Converting back to the generic type won't be as straight forward. When casting between an object and a value type, no conversions will be made and you are just boxing/unboxing the value. Since you have a constraint that the type be a value type, the casts won't really work here. In fact, I don't think it's even possible to do so generically. If a conversion exists from decimal to the type you wouldn't be able to express that here. And you won't be able to use the Convert class to do the conversion, there's no way to "add" conversions to existing convertible types.

Your best (and probably only) option would be to convert it to a dynamic variable and let the runtime figure out whether or not that conversion exists. That way if you have a custom ValueType here that has an implicit or explicit conversion from a decimal, it will be used.

return (T)(dynamic)sum;

If you know for sure that the types that are used here can be converted from a decimal, then you could convert back using IConvertible.ToType().

return ((IConvertible)sum).ToType(typeof(T), CultureInfo.InvariantCulture);
|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ Why not use IConvertible.ToType() for the conversion back? \$\endgroup\$ – svick Apr 25 '12 at 8:08
  • \$\begingroup\$ If T was a custom type (and not a builtin), the conversion would fail always since IConvertible.ToType() only considers the conversions defined by the object that implemented the interface. decimal will not have a conversion defined for your custom object and as I mentioned, it's not possible to add a conversion. This approach ensures that if the custom type provided a conversion from a decimal, it will be used. \$\endgroup\$ – Jeff Mercado Apr 25 '12 at 13:44
  • \$\begingroup\$ Jeff, thanks a bunch. I do not plan on using a custom IConvertible type here. By the way, I have to supply an IFormatProvider to the ToDecimal method. \$\endgroup\$ – Leonid Apr 25 '12 at 15:51
  • \$\begingroup\$ @svick, did you mean Convert.ChangeType? I was not sure how to invoke IConvertible.ToType() on my decimal sum. \$\endgroup\$ – Leonid Apr 25 '12 at 15:58
  • \$\begingroup\$ @Leonid: You would need to cast the decimal to IConvertible to be able to invoke that method. I'll include that detail in the answer. \$\endgroup\$ – Jeff Mercado Apr 25 '12 at 18:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.