将boxed int直接拆箱为空

时间:2011-10-04 13:43:48

标签: c# .net generics casting unboxing

我已经为处理procs返回的数据库值做了一个转换方法。它看起来像这样:

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;

    //Trata valores de enuns nuláveis. Um cast direto de um tipo numérico (Int32, por exemplo)
    //para um valor de enum lança uma exception InvalidCastException.
    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 
    //becouse 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;
}

问题是,当数据库返回一个Interger时,value变量将包含int,当Tshort时,我会得到InvalidCastException 1}}。

如何改进此方法来处理这种情况?我希望方法的用户不要担心这种问题,不必重复播放。这可能吗?

3 个答案:

答案 0 :(得分:4)

您只能将盒装值类型转换为正确的值类型,然后再次强制转换为其他值类型(如果支持此类转换)。

然而,有Convert类。如果您在int中有value框,则可以将其传递给Convert.ToInt16(value)并返回short。由于你可能使用Convert类(而不是强制转换)的类型数量很少,使用switch的静态通用辅助方法可以很好地工作,如果你使用{ {1}}。

Convert

我对数据库结果大量使用static bool MaybeConvert<TOutput>(object value, out TOutput result) { output = default(TOutput); switch(typeof(TOutput)) { case typeof(short): result = Convert.ToInt16(value); return true; ... default: return false; } } ,因为有时即使您使用的是32位整数字段,如果进行了一些数学或聚合,它们也会返回为64位整数。使用Convert比检查类型和自己进行非常具体的演员要容易得多。

答案 1 :(得分:4)

将结果用作dynamic,然后施放应该在有拳击的情况下工作。

public static T GetValue<T>(this IDataRecord record, int index)
{
    dynamic result = record[index];
    return (result == null || result == DBNull.Value) ? default(T) : (T)result;
}

顺便说一下,有一个Eric Lippert blog post简要介绍了这个。

答案 2 :(得分:2)

我找到了一种方法(请参阅代码注释以获得解释):

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;
}