AS3:强制类型转换还是“as”?

16

在使用、效率或背景技术方面是否有任何区别?

var mc:MovieClip = MovieClip(getChildByName("mc"));

var mc:MovieClip = getChildByName("mc") as MovieClip;

这个选择只是惯例、个人偏好,还是有一些情况不能使用其中一个?

8个回答

22

这篇文章 很好地描述了两者之间的不同:

在 ActionScript 2 中,当转换失败时会返回 null,而在 ActionScript 3 中,将抛出 TypeError。使用 ActionScript 3 中的 as 操作符时,无论转换是否成功,都会返回数据类型的默认值。

as 还允许你强制转换为 Array,而以前是不可能的,因为转换函数 Array() 优先级更高。

编辑: 关于性能,各种文章中提到,使用 as 比函数调用式的转换更快:[1] [2] [3]。第一篇引用的文章详细研究了性能差异,并报告了 as 快 4 倍至 4.5 倍。

编辑 2: 不仅在正常情况下使用 as 比其他方式快4到4.5倍,当你将 (cast) 样式转换包装在 try-catch 块中,并且实际上出现错误时,速度可能会更快,甚至是30到230倍。 在AS3中,如果您认为自己要做一些特殊的事情(可能会引发错误),那么清楚地表明您应该先看再跳。除非被API强制要求,否则永远不要使用try/catch,这也意味着永远不要使用(cast)。即使没有抛出异常,查看try/catch的性能影响也很有启示性。即使在没有问题的情况下,设置try/catch块也会带来性能损失。


1
嗯,根据另一个答案中链接的一些测试结果,我似乎是错的。也许这只是一个“老妇人谣言”,但我被告知使用 as 会更慢,所以一直相信着。你有什么想法吗? - Sunil D.
1
SunilD。这只是一个无稽之谈。除了Jackson Dunstan博客上的链接文章和他的测试套件外,我自己的独立测试也证实了他的结论。在所有方面,“as”确实是最佳选择。 - scriptocalypse
1
@Panzercrisis,这似乎是共识。我实际上从未编写过ActionScript - 这个答案只是通过谷歌和scriptocalypse的编辑产生的结果。 - Paul Bellora
在任何非性能关键代码中,性能差异都是微不足道的。因此,我的编程理念是编写代码以展示意图,并尽早捕获应用程序中的错误。我的规则是:在您期望转换成功的任何情况下使用构造函数转换。MovieClip(mc);仅在您预计转换在某些正常用例中失败时才使用“as”转换。这样,转换失败将立即引发警报,提醒您存在问题。 - Craig
还有,那么 "for each (var a:DisplayObject in vectorOfSprites)" 这个语句会对每一个对象执行强制类型转换吗? - joshstrike
显示剩余3条评论

4
由于目前还没有人直接回答性能方面的问题,而且这也是您的问题,as在AS3中比(cast)运行时更高效和更快。

http://jacksondunstan.com/articles/830

结合所有其他因素,我认为完全没有理由使用(cast),应该完全避免使用。

下面撤回的评论实际上也提醒了我一个好的观点。如果您使用(cast),几乎可以肯定会发现自己会陷入必须尝试/捕获的情况中。

try{
    SubType(foo).bar();
}catch(e:TypeError){
    // Can't cast to SubType
}

这很慢而且难以忍受。唯一的解决方法是先进行is检查。

if(foo is SubType){ 
  SubType(foo).bar();
}

这似乎是错误和浪费的。

1
哎呀,原先的评论被撤回了(我在平板上无法删除它,所以用这个无用的评论替换了它)。 - Sunil D.
2
实际上,使用is-check是一种明确的最佳实践。您应该始终确保转换将成功。否则,您将无论如何测试“null”,这是绝对不可取的。 - Creynders
@scriptocalypse 让我来重新表述一下:“尽可能避免使用null。”(这里就是这种情况) 有很多关于为什么null是魔鬼的文章,但简而言之:null不提供任何关于如何处理它的上下文信息。它是坏赋值、转换、未初始化成员等的结果吗?如果您尝试访问成员,AVM也会因此而出错,抛出非常难以追踪的边缘情况通用错误。此外,您会失去任何类型信息,您不知道空对象应该是什么类型。 还有...为什么感觉奇怪呢?(在强制转换之前调用is - Creynders
2
@Creynders,感觉使用is/as的主要原因是因为失败的as隐式地等于foo is Bar == false。为什么要测试两次呢?你可以这样做:var foo:Bar = x as Bar;,然后跟着写上if(foo){}。我绝不会建议像这样调用成员(foo as Bar).member() - scriptocalypse
好的,我明白了。在我的想法中,这是一个有效性检查,然后是一个操作,就像您在使用索引访问数组之前会检查数组索引一样。 - Creynders
显示剩余3条评论

2

AS3 Casting one type to another 包含了这个问题的答案:使用 "as" 关键字将一个类型转换为另一个类型时,如果转换失败,则会赋值为 null,否则会抛出 TypeError


1
(cast)和"as"是两个完全不同的东西。虽然'as'只是告诉编译器将一个对象解释为给定类型(仅适用于相同或子类或数字/字符串转换),而(cast)则尝试使用目标类的静态转换函数。这可能会失败(抛出错误)或返回目标类的新实例(不再是同一对象)。这不仅解释了速度差异,还解释了由Alejandro P.S.描述的Error事件的行为。
其含义很清楚: 如果对象的类对编码者已知但对编译器不知道(因为被一个仅命名超类或'*'的接口混淆),则建议使用'as'。如果无法100%确保假定类型(或与自动强制转换兼容的类型),则建议在之前进行'is'检查或之后进行null检查(更快)。
如果必须实际将对象转换为另一个类(如果可能),则应使用(cast)。

1

在编程中,使用as关键字是最佳实践。

as的优点是不会抛出运行时错误(RTE)。例如,假设您有一个无法转换为MovieClip的类Dog,则以下代码将引发RTE:

var dog:Dog = new Dog();
var mc:MovieClip = MovieClip(Dog);

TypeError:错误#1034:类型强制失败:无法将Dog转换为MovieClip。
为了使此代码“安全”,您必须在try / catch块中包含转换。
另一方面,使用as会更安全,因为如果转换失败,它只会返回null,然后您可以自己检查错误,而不使用try/catch块:
var dog:Dog = new Dog();
var mc:MovieClip = Dog as MovieClip;
if (mc) 
    //conversion succeeded
else
    //conversion failed

1

在使用as运算符之前,最好使用强制类型转换。只有在强制类型转换可能失败并且您希望表达式计算为null而不是抛出异常时才使用as运算符。

请这样做:

IUIComponent(child).document

不是这个:
(child as IUIComponent).document

编码规范


+1 对于引用的对立观点(尽管文章没有解释原因)。 - Paul Bellora
1
@PaulBellora 这篇文章的推理仅仅是因为它是Flex编码约定样式指南。换句话说,它完全是任意的,并且基于Flex框架作者使用的编码风格。 - scriptocalypse
我强烈反对这个。只要你在使用强制转换,它可能会因为各种原因而失败。而且使用第一种方法没有任何好处。 - Andrey Popov

0

关于启动或不启动 RTE,或返回 null,在处理加载到单独应用程序域中的 swf 中的错误时,存在显着差异。

使用 Loader.uncaughtErrorEvents 处理已加载 swf 的错误;如果我们像 'event.error as Error' 这样进行转换,则生成的错误将具有原始堆栈跟踪(与导致错误的 swf 中捕获的相同),而如果使用 Error(event.error)进行转换,则错误的堆栈跟踪将被当前堆栈跟踪更改(在其中进行了转换)。

示例代码:

if (event && event.error && event.error is Error) {
    debug ("Casting with 'as Error'") 
    debugStackTrace (event.error as Error); 
    debug ("casting with 'Error (...)'"); 
    debugStackTrace (Error (event.error)); 
}

示例输出:

Casting with 'as Error' 
ReferenceError: Error # 1056 
at Player / onEnterFrame () 
casting with 'Error (...)' 
Error: ReferenceError: Error # 1056 
at package :: HandlerClass / uncaughtErrorHandler () 
at EventInfo / listenerProxy ()

0

var mc:MovieClip = MovieClip(getChildByName("mc"));

会直接将其设置为movieclip

var mc:MovieClip = getChildByName("mc") as MovieClip;

如果必需的类型相同,将使mc的行为像movieclip一样


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