Search code examples
c#.netgenericscastingunboxing

Directly unboxing a boxed int to short


I have made a conversion method for handling the database values returned by procs. It looks like this:

public static T GetVerifiedValue<T>(this IDataRecord record, int index)
{
    object value = record[index];

    if (value is string)
        value = string.IsNullOrEmpty(value as string) ? null : value;

    Type nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(T));

    if (nullableUnderlyingType != null)
    {
        if (nullableUnderlyingType.IsEnum)
            return value == null || value.Equals(DBNull.Value) ? default(T) : (T)Enum.ToObject(nullableUnderlyingType, value);
    }

    /*
    //This is my try on solving the problem, but won't compile 
    //because T has no struct constraint
    if (value is ValueType)
    {
        ValueType structValue = (ValueType)value;
        return value.Equals(DBNull.Value) ? default(T) : (T)structValue;
    }
    */

    return value == null || value.Equals(DBNull.Value) ? default(T) : (T)value;
}

The problem is that when the database returns an Interger, the value variable will contain an int, and when T is a short, I get an InvalidCastException.

How can I improve this method to handle this situation? I want the method's user not to be concerned about this kind of problem, not having to double-cast. Is this possible?


Solution

  • I found a way using Convert.ChangeType(object, type) (see the code comments for explanation):

    public static T GetVerifiedValue<T>(this IDataRecord record, int index)
    {
        object value = record[index];
    
        if (value == null || value.Equals(DBNull.Value))
            return default(T);
    
        //This handles nullable values, because sometimes there is a need for
        //a casting on the underlying value before the final cast
        Type nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(T));
    
        if (nullableUnderlyingType != null)
        {
            if (nullableUnderlyingType.IsEnum)
                return (T)Enum.ToObject(nullableUnderlyingType, value);
            else
                return (T)Convert.ChangeType(value, nullableUnderlyingType);
        }
    
        //Enums must be handled before the ValueTypes, becouse
        //enums are also ValueTypes and using Convert.ChangeType with it will fail
        if (typeof(T).IsEnum)
            return (T)Enum.ToObject(typeof(T), value);
    
    
        //######################################################################
        //Here is the trick: as Convert.ChangeType returns an object,
        //it is compatible with the unconstrained T. Worked nicely.
        if (value is ValueType)
        {
            ValueType structValue = (ValueType)value;
            return (T)Convert.ChangeType(structValue, typeof(T));
        }
        //######################################################################
    
    
        if (value is string)
            value = string.IsNullOrEmpty(value as string) ? null : value;
    
        return (T)value;
    }