为什么 'as' 关键字有效而 () 强制转换无效?

7
//always works, returning a valid object into _page
        _page = _httpContext.Handler as System.Web.UI.Page;

//Fails throwing the exception : Unable to cast object of type 'System.Web.DefaultHttpHandler' to type 'System.Web.UI.Page'
        _page = (System.Web.UI.Page)_httpContext.Handler;

我想知道为什么会发生这种情况?

编辑:

                //Fixes the problem
            if(_httpContext.Handler is System.Web.UI.Page)
            _page = (System.Web.UI.Page)_httpContext.Handler;

如果我调试'as'关键字语句,我永远不会得到空引用(对象总是被正确分配)。然而,()转换除非有if语句否则会创建异常。
编辑:在大约15次类运行后,我能够获得一个null。似乎需要更多的运行才能找到null,与()转换捕获异常的速度相比较奇怪。
旧版:当在'as'语句中进行调试时,每次类运行时断点都会触发-从不为空。
当if语句中的'()'语句进行调试时,每次断点触发时,转换正常工作。奇怪。
9个回答

14
//总是有效地工作,将一个有效的对象返回到_page变量中 _page = _httpContext.Handler as System.Web.UI.Page;
这并不是技术上的有效方法。如果您注意到,_page将是null。它只是没有抛出错误。
“as”运算符用于告诉应用程序“我希望您尝试转换此对象。可能不会成功,我知道这一点,因此请勿引发异常。我会相应处理它。”
“()”转换用于告诉应用程序,“该对象可以转换为此类型。如果不能,则说明有问题,我需要了解详情。”
两种转换之间的区别(以及何时使用它们)在于当您“认为”某些内容可转换为另一种类型时以及何时您“知道”某些内容可转换为另一种类型。
以下是Eric Lippert撰写的关于此主题的文章(已更改为他的博客而非重新提供):http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx

7

从这里开始:

在C#中,使用as运算符与强制类型转换有三个重要的区别:

当您尝试转换的变量不是所请求的类型或其继承链中的类型时,它会返回null,而不是抛出异常。它只能应用于将引用类型转换为引用类型的变量。使用as不会执行用户定义的转换,例如隐式或显式转换运算符,而强制类型转换语法会执行这些操作。实际上,在处理这两个关键字(castclass和isinst指令)时,IL定义了两个完全不同的操作——这并不仅是C#编写“语法糖”以获得这种不同的行为。在Microsoft的CLR v1.0和v1.1中,as运算符似乎比强制类型转换略快,即使在没有无效转换的情况下,由于异常而降低强制类型转换的性能。


但是为什么强制转换不起作用呢?我知道Handler是System.Web.UI.Page。我也谷歌过并阅读了你发布的内容。但是,仍然感觉没有意义。 - Mausimo
“它只能应用于将引用类型变量转换为引用类型”实际上并不准确 - 值可以是任何类型,并且目标类型也可以是可空值类型。例如,(int)0 as IEquatable<int> 是有效的,特别是 (object)o as int? 是一种很好的方法来检查 o 是否是装箱的 int 而不捕获异常。它还允许您编写诸如 (T?)x as U? 这样的代码,在泛型代码中可能会有些用处。 - Pavel Minaev
3
@Maus,它实际上不起作用。第一行实际上会将null放入_page中,这看起来不像您预期的那样。 - Earlz
如果转换失败,则 _handler 不是 System.Web.UI.Page。在异常点处使用调试器检查它指向了什么。 - Pavel Minaev
@所有人 - 请阅读编辑。 @Earlz,我调试了第一行,它从未得到null。 - Mausimo
@Earlz - 对不起,经过大约15次运行该类后,我终于能够得到一个空值。 - Mausimo

4

as cast和prefix cast之间的一个主要区别在于,如果使用prefix cast会抛出一个异常,而如果使用as cast则会返回null。


0
如果您使用'as'运算符,如果转换失败,它将返回null。如果您进行显式转换,则在转换失败时会引发异常。在这种情况下,'as'向下转换为所期望的正确类型。显式转换无法上移到所需的类型。
通常情况下,您应该始终使用'as',除非您真正需要显式转换(例如当您需要转换为值类型时)。

我并不信服。我宁愿使用Class1 foo = (Class1)bar进行强制转换并获得转换异常,也不愿在稍后使用foo.Property或foo.Method()时获得一个无用的空异常。那个空异常可能会在“as”的范围之外的任何地方发生。如果我预计代码有时会失败,那么我会使用“as”并检查是否为空。 - Nelson Rothermel
那是一般的模式。你用“as”进行转换,然后检查是否为空。 - Tejs

0

使用 as 尝试将该对象转换为特定类型,但在失败时返回 null 而不是抛出异常(而强制转换会直接抛出异常)。你确定带有 as 子句的对象实际上返回了非空对象吗?


0
与@Tejs的答案相同。使用as失败的转换会产生null值。
我要说的和他不同,一般规则是始终使用显式类型转换。我个人更喜欢在转换失败的位置直接抛出异常,而不是在另一页上得到一个(看似)毫无头绪的null引用异常。 as操作符的一个很好的用途是将东西从数据库转换出来时,不想检查System.DBNull
int someint = cmd.ExecuteScalar() as int? ?? 0;

0

0

实际上,这正是应该发生的事情。强制转换失败并抛出错误,因为强制转换无效。如果强制转换失败,as 会默认为 null。


0

正如其他答案所指出的那样,问题归结为“as”在无效转换的情况下返回null,而显式转换会抛出异常

其他答案对于倾向于使用哪种方法有一些争议。如果您要使用“as”,则需要在某个时刻处理它可能为空的事实。如果您要使用显式转换,则必须处理可能的异常。根据用例,我倾向于使用其中之一。

当我知道转换将起作用但编译器不知道,并且抛出异常是可以接受的(因为这是一个特殊情况)时,我倾向于使用特定的转换。但是,如果存在合理的机会使转换无效,我更喜欢使用“as”并测试是否为空。这避免了捕获异常,这在我的看法中更加丑陋并使调试更加困难。


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