可空装箱类型可以转换为枚举类型,但装箱的枚举类型不能转换为可空类型。

5
  • 可装箱的可空基础类型可以被强制转换为枚举类型,但可装箱的枚举类型无法被强制转换为可空类型。

同样地,

  • 可装箱的可空枚举可以被强制转换为基础类型,但可装箱的基础类型无法被强制转换为可空枚举。

好的,我知道“可装箱的可空类型”不是描述它的最佳方式,但这是为了问题而设定的。我知道被装箱的是基础值类型。

我将用示例来说明。假设我有一个enum,其中int是基础类型。

enum Sex { Male, Female }

案例一:

int? i = 1;
object o = i;
Sex e = (Sex)o; //success

//but

Sex e = Sex.Male;
object o = e;
int? i = (int?)o; //invalid cast

案例二:

Sex? e = Sex.Male;
object o = e;
int i = (int)o; //success

//but

int i = 1;
object o = i;
Sex? e = (Sex?)o; //invalid cast

简而言之,
(enum)int? -> succeeds
(int?)enum -> the reverse fails

(int)enum? -> succeeds
(enum?)int -> the reverse fails

甚至更简单地说,

转换为非可空类型 -> 成功
转换为可空类型 -> 失败

现在我知道一旦你将值类型装箱,它只能转换回原始类型。但由于根据C#规则,一个装箱的 int 可以转换为 enum,一个装箱的 enum 可以转换为 int,一个装箱的 int 可以转换为 int?,而一个装箱的 int? 可以转换回 int,所以我正在寻找其他场景的一致性理解,就像上面列出的那些场景一样。但我不明白其中的逻辑。首先,如果它们全部失败或全部成功,这对开发人员来说会更有意义。 其次,即使是成功的转换,看起来也有点奇怪。我的意思是,由于值类型可以隐式地转换为其可空等效类型(而不是相反),转换为可空类型应该总是成功的,但是在当前的实现中,可空类型正在被成功地转换为非可空类型,如果前者为null,则可能会失败。如果整个过程反过来,那么理解起来会更容易。像这样的一个例子:

Sex? e = null;
object o = e;
int i = (int)o; //succeeds, but sure to explode on cast

//but

int i = 1;
object o = i;
Sex? e = (Sex?)o; //invalid cast, even though its always a safe cast

问题:

  1. 那么是哪个C#规则让这种情况发生的?

  2. 有没有简单的方法可以帮助我记住这个规则?


1
好问题。我认为这实际上是CLR级别的差异,而不是C#级别的差异。有一些在CLR级别上是有效的东西,在C#级别上是不允许的 - 比如在int[]uint[]之间进行强制转换。 - Jon Skeet
1个回答

4

我认为这是unboxunbox.any指令的微妙之处。

来自ECMA 335,第III.4.32节(unbox操作 - unbox.any类似)

异常:
如果obj不是装箱的值类型,valuetypeNullable<T>并且obj不是装箱的T,或者obj中包含的值的类型不是verifier-assignable-to(III.1.8.2.3)valuetype,则抛出System.InvalidCastException

例如,在这种情况下:

Sex e = Sex.Male;
object o = e;
int? i = (int?)o;

因为valuetypeNullable<int>类型,而obj的值不是一个装箱的int类型,所以它完全正确地失败了。对于Nullable<T>类型,"verifier-assignable-to"部分无法应用。

不幸的是,我怀疑C#规范中没有描述任何这种行为——我认为从“装箱的int”到“具有底层类型int的枚举”的拆箱行为并没有被描述,至少我没看到,而这是混合使用可空性的一种先决条件。


规格忍者出击! :) 很抱歉回复晚了。确实是个好发现。虽然我不完全确定为什么它被设计成这样,有点令人困惑。 - nawfal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接