为什么 C# 不能将包含盒装 int 的对象强制转换为双精度值?

Why can't C# cast an object containing a boxed int to a double?

提问人:Gilles jr Bisson 提问时间:7/27/2023 最后编辑:Alexei LevenkovGilles jr Bisson 更新时间:7/30/2023 访问量:177

问:

在 C# 中,为什么示例 1 有效:

int myValue1 = 11;
double resultDirectlyFromInt = myValue1;

但示例 2 没有:

int myValue2 = 22;
object myObject2 = myValue2;
double resultFromBoxedInt = (double)myObject2;

示例 3 再次起作用:

double myValue3 = 33.3;
object myObject3 = myValue3;
double resultFromBoxedDouble = (double)myObject3;

有人可以解释这背后的理由吗?因为对我来说,工作示例 1 和 3 看起来像是示例 2 应该有效的证明。

C# 强制转换 整数

评论

0赞 jdweng 7/27/2023
可以使用 Convert.ToDouble(myObject2)。c# 语言不支持从 int 强制转换为 double。C# 支持在不强制转换的情况下将 int 设置为双精度值。这只是语言中的一个怪癖。
1赞 Jon Skeet 7/28/2023
“c# 语言不支持从 int 到 double 的强制转换。”是的,它确实如此,无论是隐式转换还是显式转换。它不支持将装箱的 int 拆箱为 double。

答:

5赞 Jon Skeet 7/27/2023 #1

因为对我来说,工作示例 1 和 3 看起来像是示例 2 应该有效的证明。

不,它们是非常不同的转换。

C# 定义了从 to 的转换(请参阅 C# 7 标准草案的第 10.2.3 节)。但是,在第 10.3.7 节中,对拆箱转化的定义大不相同:intdouble

对non_nullable_value_type的拆箱操作包括首先检查对象实例是否为给定non_nullable_value_type的装箱值,然后将该值从实例中复制出来。

...

如果源操作数是对不兼容对象的引用,则抛出 a。System.InvalidCastException

在示例 2 中,对象实例不是 类型的装箱值 - 它是 类型的装箱值,因此上述检查失败。doubleint

碰巧的是,.NET CLR 在拆箱方面比 C# 要求的更宽容一些;您可以从 Boxed 解箱到 A,反之亦然,也可以从 Enum 解箱到其基础类型,反之亦然。但它不允许任何需要表示更改的东西,比如 .intuintintdouble

如果要为这些规则提供理由,可以这样想:为了执行执行实际工作的任何转换(而不是仅仅将位模式从一个位置复制到另一个位置),编译器需要告诉 CLR 要执行哪个转换。对于取消装箱操作,实际类型仅在执行时已知,因此编译器无法指示 CLR 执行的操作。CLR 可以有更复杂的规则,以便它可以根据执行时间类型执行转换 - 但这偏离了通常基于语言的领域。

评论

0赞 Alexei Levenkov 7/28/2023
为没有对你的答案投赞成票而感到难过:)......虽然我喜欢(并同意)理由部分(这似乎是 OP 所要求的),但我认为它不够权威。我不知道这样的解释是否真的存在于某个地方,所以......
0赞 Gilles jr Bisson 7/30/2023
谢谢@Jon Skeet。我现在明白了这个概念。
0赞 Gilles jr Bisson 7/30/2023
@AlexeiLevenkov 虽然指向 thedocs 的链接很有价值,但我摔倒了,你的编辑改变了我问题的语气,这是不必要的。也许链接和隐含的评论在答案中会更好。
0赞 Alexei Levenkov 7/30/2023
@GillesjrBisson对不起。下次会继续我的“投反对票并继续前进”。感谢您的提醒。
0赞 WWW 7/28/2023 #2

您的第一个示例实际上是调用名为 的运算符,您可以在此链接中找到更多信息,而这恰好是在编译器级别实现的。它之所以有效,是因为编译器知道整数可以在编译时完全表示在 -type 变量中。implicit conversion operatorInt32Double

在示例 2 中,它不起作用的原因是在开箱时,必须先将其强制转换为原始类型,以便运行时可以保证类型安全。也可以参考其他答案。 相反,您可以按如下方式更改示例 2,以便它将在没有运行时抱怨的情况下运行,这在某种程度上解释了示例 1 如何在后台工作,强制转换加倍,但明确。

int myValue2 = 22;
object myObject2 = myValue2;
double resultFromBoxedInt = (double)(int)myObject2;

评论

0赞 Gilles jr Bisson 7/30/2023
我不能在我的项目中这样做的原因是,我试图转换为 double 的盒装值最初可能是 int 或 double。所以我想 Convert.ToDouble() 是我要走的路。