【问题标题】:Directly unboxing a boxed int to short直接将装箱的 int 拆箱为短
【发布时间】:2011-10-04 13:43:47
【问题描述】:

我已经做了一个转换方法来处理 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

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

【问题讨论】:

  • 不要使用unboxing术语代替castingunboxing表示引用类型->值类型
  • 这就是正在发生的事情:value 变量,它是一个 object,包含一个 int。然后我将其转换为short。不是unboxing变量上的int值吗?
  • @Raphael:好的,但在这种情况下,你可以扩展标题吗,因为它真的很混乱unboxing int to short,就像object (int) -&gt; short
  • 按照@sll 的建议更改了问题的标题。
  • @Icarus: object 是一个引用类型,所以这个拆箱(objectint)。请参阅here 了解更多信息。之后,将发生从intshort演员表

标签: c# .net generics casting unboxing


【解决方案1】:

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

但是,有 Convert 类。如果您在value 中有一个装箱的int,您可以将它传递给Convert.ToInt16(value) 并返回一个short。由于您可能使用Convert 类(而不是强制转换)的类型数量很少,如果您使用Convert,使用switch 的静态通用帮助器方法会很好用。

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 比检查类型和自己进行非常具体的转换要容易得多。

【讨论】:

    【解决方案2】:

    将结果用作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 简要介绍了这个问题。

    【讨论】:

    • 这是一个非常好的方法,但遗憾的是,我们在这里使用 3.5 框架版本,所以没有dynamic... :(
    • 我已经测试了这个解决方案,但它不起作用。我收到了一个 RuntimeBinderException 声明 Operator '==' cannot be applied to operands of type 'int' and 'System.DBNull'
    • 啊,我没有尝试可以为空的东西。为什么不修改== 检查以改用object.Equals?这几乎不会失败。
    • 使用 result.Equals(DBNull.Value) 工作 :)
    【解决方案3】:

    我找到了一种使用Convert.ChangeType(object, type)的方法(解释见代码cmets):

    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-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多