关于c#:为什么我不能将int取消装箱作为小数?

Why can't I unbox an int as a decimal?

我有一个IDataRecord reader,我从以下内容中提取小数:

1
decimal d = (decimal)reader[0];

出于某种原因,这会引发一个无效的强制转换异常,表示"指定的强制转换无效"。

当我这样做时,它告诉我它是一个整数。据我所知,这不应该是个问题……

我用这段代码测试了这一点,效果很好。

1
2
int i = 3750;
decimal d = (decimal)i;

这让我抓耳挠腮,想知道为什么它不能将读卡器中包含的int作为一个十进制值拆封。

有人知道为什么会发生这种情况吗?有什么我不知道的吗?


只能将值类型取消绑定到其原始类型(以及该类型的可空版本)。

顺便说一下,这是有效的(只是两行版本的简写):

1
2
object i = 4;
decimal d = (decimal)(int)i; // works even w/o decimal as it's a widening conversion

出于这个原因,阅读这篇埃里克·利珀特的博客文章:代表性和身份

就我个人而言,我将通过强制转换语法完成的操作分为四种不同的操作类型(它们都有不同的IL指令):

  • 装箱(boxil指令)和拆箱(unboxil指令)
  • 通过继承继承(如C++中的EDOCX1 2),使用EDCOX1×3的IL指令进行验证
  • 在原始类型之间进行转换(如C++中的EDCOX1×4),对于原始类型之间的不同类型的转换,有很多IL指令。
  • 调用用户定义的转换运算符(在IL级别,它们只是对适当的op_XXX方法的方法调用)。

  • 这是一个简单的解决方案。它负责拆箱,然后转换为小数。对我来说工作很好。

    1
    decimal d = Convert.ToDecimal(reader[0]);  // reader[0] is int


    int强制转换为decimal时没有问题,但是在解除对象绑定时,必须使用对象所包含的确切类型。

    要将int值取消装箱为decimal值,首先将其作为int取消装箱,然后将其转换为十进制:

    1
    decimal d = (decimal)(int)reader[0];

    IDataRecord接口还具有取消绑定值的方法:

    1
    decimal d = (decimal)reader.GetInt32(0);


    阿夫沙里说:

    You can only unbox a value type to its original type (and the nullable
    version of that type).

    要认识到的是,铸造和拆箱之间有区别。杰里耶瓦尔说得很好

    In a sense it's a shame that unboxing and casting syntactically look
    identical, since they are very different operations.

    铸造:

    1
    2
    int i = 3750; // Declares a normal int
    decimal d = (decimal)i; // Casts an int into a decimal > OK

    装箱/拆箱:

    1
    2
    object i = 3750; // Boxes an int ("3750" is similar to"(int)3750")
    decimal d = (decimal)i; // Unboxes the boxed int into a decimal > KO, can only unbox it into a int or int?