为什么拆箱需要在 C# 中进行显式转换?

Why does unboxing require explicit casting in C#?

装箱是将值类型转换为托管堆对象的过程,它是隐式的。拆箱是相反的过程,编译器需要显式转换。由于装箱存储数据类型,为什么拆箱不能使用它而不是要求显式转换?

class BoxUnBox
{
 static void Main()
 {
   int i = 123;      // a value type
   object o = i;     // boxing
   int j = (int)o;   // unboxing - Why is an explicit cast required?
 }
}

任何整数都可以转换为对象。并非所有对象都可以转换为整数。

如果有人把 o 的内容改成 "Hello World" 怎么办?为确保您知道自己在做什么,编译器要求您显式转换装箱值。

基本上,隐式转换意味着对象类型的任何实例 o 也可以表示为 int 的实例,但显然不是这种情况。举个例子:

int i = -1;
long j = i;

很明显你的变量i是一个整数,也可以认为是long。这就是隐式转换在这里是准确的原因。另一方面,并​​非每个 long 都可以转换为 int 而不会丢失任何数据 。因此,您需要一个显式转换来确定:我知道 可能 会丢失一些数据,但我不关心它。

因为 Int32 一个 Object,但是 Object 可能是 Int32。 第一种情况编译器知道要做什么,但是第二种情况你要告诉编译器你知道你在做什么,并且保证可以进行拆箱。

继承关系是定向的!父子不一样

编译器不能保证你的对象里面有什么。这就是为什么您需要显式转换为您期望的值。对于编译器:

这很危险

object o = 45;
int j = (int)o;

像这样:

object o = "something";
int j = (int)o;

编译时不允许这样做。

您的问题与开箱操作无关。实际上它应该听起来像 "Why should I use explicit conversion?" 考虑下面的例子:

int i = 123;
long l = i;
int j = (int)l; // OMG why??

答案很简单,您可以在 C# 规范中找到它 6.2 Explicit conversions:

The explicit conversions are conversions that cannot be proven to always succeed, conversions that are known to possibly lose information, and conversions across domains of types sufficiently different to merit explicit notation.

在上面的示例中,您可能 丢失信息 ,因为 long 可以保存不适合 int 范围的值。但是将 int 分配给 long:

时,您永远不会丢失信息
long l = i; // safe

在您的示例中,您需要显式转换,因为隐式转换无法证明总是成功object类型的变量可以引用 几乎任何类型。字符串呢?

object o = i;  // implicit and always safe
o = "Now I have a machinegun ho-ho-ho"; // safe too
int j = o;     // will not succeed if o is string

类比

对象变量就像一个黑盒子,您可以在其中放置任何东西 - 音乐 CD、笔、phone 或香蕉。不仅是你,任何人都可以把东西放在那里。如果你早上最后放进黑盒子里的东西是香蕉,你晚上回来吃你从黑盒子里拿出来的东西吗?如果你一个人住,而且房间是封闭的,而且你的记忆力非常好,那么......那么你可以。你会想知道为什么每个人在吃之前都会检查他们盒子里的东西。但是如果你不是一个人住,或者房间没有关门,或者你可以忘记你曾经把phone放进盒子里……好胃口

转换可能会在运行时失败,这取决于您的 object 实际包含的内容。当隐式拆箱成为可能时,您可能会忽略错误,因为您可能编写了一些含义不同的内容(您自己或您误解了其他人的代码)。编译器要求您显式转换,因为您应该真的想要 进行转换。否则你可能会错误地混合类型,这会产生非常容易出错的代码。通过强制强制转换,如果你做的是正确的,你将被迫三思而后行。

使用 Dynamic 避免强制转换

我不想删掉已经说过的任何内容,但我想指出,从技术上讲,执行拆箱并不总是需要演员表。 dynamic 关键字允许系统自动执行拆箱和转换。我既不推荐也不反对使用 dynamic,只是指出它的行为。

static void DynamicTest()
{
    int i = 123;      // a value type
    object o = i;     // boxing
    dynamic d = o;    // shift to dynamic
    int j = d;        // unboxing - No cast required
}

编辑:Jeroen Mostert 明智地指出 dynamic 关键字并不是任何一种总能使它起作用的魔法。它只是将评估推迟到运行时行为。所以,虽然上面的例子总是有效,但更复杂的例子肯定会失败。因此,在使用 dynamic 关键字和预期 (try/catch) 运行时失败时必须小心。如果使用得当,动态关键字可以 none-the-less 成为一个非常强大的工具。