C# 直接将装箱的int拆箱为short

C# 直接将装箱的int拆箱为short,c#,.net,generics,casting,unboxing,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;

我制作了一个转换方法来处理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;
}
public static T GetVerifiedValue(此IDataRecord记录,int索引)
{
对象值=记录[索引];
if(值为字符串)
value=string.IsNullOrEmpty(值作为字符串)?null:value;
Type nullableUnderlyngType=Nullable.getUnderlyngType(typeof(T));
if(NullableUnderlineType!=null)
{
if(NullableUnderlineType.IsEnum)
返回值==null | | value.Equals(DBNull.value)?默认值(T):(T)Enum.ToObject(NullableUnderlineType,value);
}
/*
//这是我解决问题的尝试,但不会编译
//因为T没有结构约束
如果(值为ValueType)
{
ValueType structValue=(ValueType)值;
返回值.Equals(DBNull.value)?默认值(T):(T)structValue;
}
*/
返回值==null | | value.Equals(DBNull.value)?默认值(T):(T)值;
}
问题是当数据库返回一个整数时,
变量将包含一个
int
,当
T
是一个
short
时,我得到一个
InvalidCastException


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

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

但是,还有
Convert
类。如果在
value
中有一个已装箱的
int
,则可以将其传递到
Convert.ToInt16(value)
并返回
short
。由于您可能使用
Convert
类(而不是强制转换)的类型数量很少,如果您使用
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;
    }
}
静态布尔可能转换(对象值,输出结果){
输出=默认值(TOutput);
开关(类型(TOutput)){
案例类型(简称):
结果=转换为16(值);
返回true;
...
违约:
返回false;
}
}

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

将结果用作
动态
,然后在装箱的情况下进行强制转换

public static T GetValue<T>(this IDataRecord record, int index)
{
    dynamic result = record[index];
    return (result == null || result == DBNull.Value) ? default(T) : (T)result;
}
public static T GetValue(此IDataRecord记录,int索引)
{
动态结果=记录[索引];
返回(result==null | | result==DBNull.Value)?默认值(T):(T)结果;
}

顺便说一句,Eric Lippert简要介绍了这一点。

我找到了一种使用
转换.ChangeType(对象,类型)
的方法(请参阅代码注释以获取解释):

public static T GetVerifiedValue(此IDataRecord记录,int索引)
{
对象值=记录[索引];
if(value==null | | value.Equals(DBNull.value))
返回默认值(T);
//这将处理可为空的值,因为有时需要
//在最终转换之前对基础值进行转换
Type nullableUnderlyngType=Nullable.getUnderlyngType(typeof(T));
if(NullableUnderlineType!=null)
{
if(NullableUnderlineType.IsEnum)
返回(T)Enum.ToObject(NullableUnderlineType,value);
其他的
返回(T)Convert.ChangeType(值,NullableUnderlineType);
}
//枚举必须在ValueTypes之前处理,因为
//枚举也是ValueType,对其使用Convert.ChangeType将失败
if(类型(T).IsEnum)
返回(T)Enum.ToObject(typeof(T),value);
//######################################################################
//技巧如下:当Convert.ChangeType返回一个对象时,
//它与无约束T兼容。工作良好。
如果(值为ValueType)
{
ValueType structValue=(ValueType)值;
return(T)Convert.ChangeType(structValue,typeof(T));
}
//######################################################################
if(值为字符串)
value=string.IsNullOrEmpty(值作为字符串)?null:value;
返回(T)值;
}

不要使用
取消装箱
术语而不是
铸造
取消装箱
平均参考类型->值类型发生的情况是:
变量,它是一个
对象
,包含一个
int
。然后我将它转换成一个
short
。是不是变量上的
int
unboxing
呢?@Raphael:好的,但在这种情况下,你能扩展标题吗?因为它确实让人困惑
unboxing int to short
,比如
object(int)->short
更改了问题标题的清晰性,正如@sll所建议的那样。@Icarus:
object
是一种引用类型,所以这就是解装箱(
object
int
)。有关更多信息,请参阅。之后,将从
int
转换为
short
。这是一种非常好的方法,但遗憾的是,我们在这里使用的是3.5框架版本,因此没有
动态
:(我已经测试了这个解决方案,但它不起作用。我得到了一个
RuntimeBinderException
声明
运算符'==''不能应用于'int'和'System.DBNull'类型的操作数。
。啊,我没有尝试可为null的东西。为什么不修改
=
检查以使用
对象。Equals
?这几乎不会失败。使用
result.Equals(DBNull.Value)
:)
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;
}