C#如何将类型转换为可空类型?

36

除了普通的CastAs之间的差异外,还有更多。

  • 如果我知道apple是一个Fruit,我可以使用(Fruit)apple - 如果不是,它会抛出异常
  • as value可以检查是否为null以查看是否成功[不会抛出异常...]

然而,我一直在阅读@EricLippert的文章,其中有一个关于可空值类型的好样例:

short? s = (short?)123;
int? i = s as int?;

这段代码不能编译通过...

无法通过引用转换、装箱转换、拆箱转换、包装转换或空类型转换将类型 "short?" 转换为 "int?"

好的。

那么这个呢:

    short? s = (short?)123;
    int? i = (int?)s;

编译是否成功?(全然出乎意料!我知道s不是int?类型,它应该会挂掉,但它却没有……)

这里的类型转换检查要比前面的例子更加致命(那个例子挂掉了)。

我很抱歉要问这个被讨论得如此频繁的主题。

提前致谢。


1
你描述的正是Eric Lippert所写的 - 对于可空类型,强制转换将会编译通过,而as运算符则不会。 - Yahia
还有一件事 - int? i2 = (object)s as int?; - 实际上返回了 null - NSGaga-mostly-inactive
好的,我想我已经明白你的问题了...让我们试试这个:(int?)不会在s不是int?时抛出异常。它的工作是在s不能转换为int?时抛出异常。有很多情况下,(type)风格的强制转换可以做一些其他事情,而不仅仅是测试是否拥有该类型(例如,您甚至可以定义自己的类型--http://msdn.microsoft.com/en-us/library/xhbhezf4(v=vs.100).aspx)。如果您真的想要测试您拥有的是否是类型的实际实例,您应该使用`is`运算符。 - sblom
1
换句话说,在第一个例子中,s as int?,你试图转换s本身。在第二个例子中,(int?)s,你取s的值并尝试转换它。这样讲通了吗? - Mr Lister
1
@RoyiNamir 尝试编译 int i = 123 as int;,你会得到一个错误,提示为:"The as operator must be used with a reference type ('int' is a value type)"。而 int i = (int)123; 则可以完美地编译。 - Mr Lister
显示剩余6条评论
5个回答

33
在您的第一个示例中,as运算符尝试将对象s用作int?。由于int?不在short?的继承链中,因此此操作失败。
在您的第二个示例中,实际上是使用short? s的值创建了一个新的int? i。这是一种更慷慨的操作,因为它不必保留左侧的原始s对象。
重要的一点是as不能执行任何不保留对象身份的操作。显式转换可以。
以下是C#标准关于(int?)表单如何工作的说明:
6.1.4 隐式可空类型转换
对非可空值类型进行的预定义隐式转换也可以用于这些类型的可空形式。针对从非可空值类型S到非可空值类型T进行的每个预定义隐式标识和数字转换,都存在以下隐式可空类型转换:
·从S?到T?的隐式转换。
·从S到T?的隐式转换。
基于从S到T的底层转换的隐式可空类型转换的评估如下:
·如果可空转换是从S?到T?:
o 如果源值为null(HasValue属性为false),则结果是类型T?的null值。
o 否则,将评估该转换作为从S?到S的解包装,然后是从S到T的底层转换,最后是从T到T?的包装(§4.1.10)。
·如果可空转换是从S到T?那么该转换将被评估为从S到T的底层转换,后跟从T到T?的包装。

除了这个解释之外,还有这一行(int?)s,它确实与继承/装箱时代有关...但它可以工作,这就是我不理解的事情。 - Royi Namir
@RoyiNamir,我真的不太明白。在C、Java和几乎所有其他语言中,从shortint存在转换(这是事实),而C#规范描述了如何将该转换应用于可空类型。(我在上面添加了相关摘录。) - sblom
1
@sblom 我认为你应该给上面的段落加粗,这样说重要的是,as不能执行任何不保留对象标识的操作,而显式转换可以。- 我认为这是核心。至于允许转换,我同意,对我来说很清楚(实际上 as并不是很清楚),就像Eric所说,short-s没有算术运算(对此并不重要),它会将其转换为int。 - NSGaga-mostly-inactive
实际上,“as int?”无法编译的原因是它不是有效的语法。C#不尝试允许可空值与“as”一起使用;编译器不尝试使这样的转换成为可能。例如,我想“我要么有一个‘装箱的int’,要么希望结果变为‘null’”,但编译器无法处理这种情况。 - ToolmakerSteve

4

示例:

int? i = (int?)s;

编译器因为强制类型转换是你告诉编译器你知道它无法推断的事情,也就是说,s可以转换为int?

只有在强制类型转换失败时才会在运行时出现异常。


@RoyiNamir - 抱歉,我应该补充说明只有在强制转换无效的情况下才会发生这种情况。 - Oded

0

我通过反射找到了这个可能的解决方案。 思路是:

    var notNullableValue = 3;
    var someNullableType = typeof(Nullable<int>);
    var nullableValue = Activator.CreateInstance(someNullableType, new[] { notNullableValue });

在我的情况下,我无法使用直接转换,因为我正在使用任意类型和反射来创建对象并填充所有属性。

0

我认为这是因为在as失败的情况下,你将得到一个“有效”的null结果,因此会出现假阳性。在第二种情况下,强制转换是允许的,因为在失败的情况下它会引发异常。


0
原因是int?只是System.Nullable的简写(System.Nullable是类型)。这个简短的类型定义了一个显式转换为int,然而System.Nullable没有任何这样的显式转换,因为T可能是任何其他值类型。

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