2

我已经制定了一种转换方法来处理 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;

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

问题是当数据库返回一个整数时,value变量将包含一个int,当T是一个时short,我得到一个InvalidCastException

我该如何改进这种方法来处理这种情况?我希望该方法的用户不必担心这种问题,而不必双重施法。这可能吗?

4

3 回答 3

4

您只能将装箱的值类型转换为正确的值类型,然后再次转换为不同的值类型(如果支持这种转换)。

但是,有Convert类。如果你有一个 boxed intin value,你可以将它传递给Convert.ToInt16(value)并得到一个short返回。由于您可能使用Convert该类(而不是强制转换)的类型数量很少switch,如果您使用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;
    }
}

Convert在数据库结果中使用了很多,因为有时即使您使用 32 位整数字段,如果完成了一些数学或聚合,它们也会作为 64 位整数返回。使用Convert.ToInt32比检查类型和自己进行非常具体的转换要容易得多。

于 2011-10-04T13:58:16.330 回答
3

在存在拳击的情况下,使用结果作为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博客文章简要介绍了这一点。

于 2011-10-04T14:01:11.197 回答
2

我找到了一种使用方法Convert.ChangeType(object, type)(请参阅代码注释以获取解释):

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;
}
于 2011-10-04T15:11:03.323 回答