代码之家  ›  专栏  ›  技术社区  ›  Heinzi

即使没有空检查,使用“as”而不是强制转换是否有意义?[关闭]

  •  348
  • Heinzi  · 技术社区  · 16 年前

    在开发博客、在线代码示例(最近)甚至是一本书中,我经常遇到这样的代码:

    var y = x as T;
    y.SomeMethod();
    

    或者,更糟的是:

    (x as T).SomeMethod();
    

    这对我来说没有意义。如果你确定 x 属于类型 T ,您应该使用直接演员表: (T)x . 如果您不确定,可以使用 as 但需要检查 null 在执行某些操作之前。上面的代码所做的就是将 InvalidCastException 变成(无用的) NullReferenceException .

    我是唯一一个认为这是公然滥用 作为 关键字?或者我错过了一些明显的东西,而上面的模式实际上是有意义的?

    13 回复  |  直到 12 年前
        1
  •  249
  •   Mehrdad Afshari    16 年前

    你的理解是真的。对我来说,这听起来像是在尝试微观优化。当您确定类型时,应该使用普通的强制转换。除了生成更合理的异常之外,它也很快失败。如果您对类型的假设是错误的,那么您的程序将立即失败,您将能够立即看到失败的原因,而不是等待 NullReferenceException ArgumentNullException 或是将来某个时候的逻辑错误。一般来说,一个 as 表达式后面没有 null 检查某个地方是否有代码味道。

    另一方面,如果您不确定演员阵容,并希望它失败,您应该使用 作为 而不是用 try-catch 块。此外,使用 作为 建议在类型检查后加上强制转换。而不是:

    if (x is SomeType)
       ((SomeType)x).SomeMethod();
    

    它产生一个 isinst instruction 对于 is 关键字和 castclass instruction 对于强制转换(有效地执行强制转换两次),您应该使用:

    var v = x as SomeType;
    if (v != null)
        v.SomeMethod();
    

    这只会生成一个 伊辛 指令。前一种方法在多线程应用程序中存在潜在缺陷,因为竞争条件可能导致变量在 检查成功,但在转换行失败。后一种方法不容易出现这种错误。


    以下解决方案是 未推荐的 用于生产代码。如果你真的讨厌C语言中的这种基本结构,你可以考虑切换到VB或其他语言。

    如果一个人非常讨厌转换语法,他/她可以编写一个扩展方法来模拟转换:

    public static T To<T>(this object o) { // Name it as you like: As, Cast, To, ...
        return (T)o;
    }
    

    用干净的语法:

    obj.To<SomeType>().SomeMethod()
    
        2
  •  42
  •   Rubens Farias    16 年前

    IMHO, as 当与 null 检查:

    var y = x as T;
    if (y != null)
        y.SomeMethod();
    
        3
  •  39
  •   Larry Fix    16 年前

    使用“as”不应用用户定义的转换,而强制转换将在适当的情况下使用它们。在某些情况下,这可能是一个重要的区别。

        4
  •  36
  •   Eric Lippert    16 年前

    我在这里写了一点:

    http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx

    我理解你的观点。我同意它的观点:一个强制转换操作符传递“我确信这个对象可以转换成那个类型,如果我错了,我愿意冒异常风险”,而一个“as”操作符传递“我不确定这个对象是否可以转换成那个类型;如果我错了,给我一个空值”。

    然而,有一个微妙的区别。(x为t),whatever()传递“我不仅知道x可以转换为t,而且这样做只涉及引用或取消绑定转换,而且x不是空的”。它传递的信息与((t)x).whatever()不同,也许这就是代码作者的意图。

        5
  •  16
  •   to StackOverflow    16 年前

    我经常看到提到 this misleading article 证明“as”比铸造更快。

    这篇文章中一个更明显的误导性方面是图形,它没有指明被测量的是什么:我怀疑它是在测量 失败 强制转换(其中“as”明显更快,因为没有异常被抛出)。

    如果你花时间去测量,你就会发现,正如你所期望的那样, 更快 而不是当演员成功的时候。

    我怀疑这可能是“货物崇拜”使用as关键字而不是cast的一个原因。

        6
  •  11
  •   Joey Gumbo    16 年前

    直接演员组需要一对括号,而不是 as 关键字。因此,即使在100%确定类型的情况下,它也可以减少视觉混乱。

    不过,同意了例外情况。但至少对我来说 作为 沸腾检查 null 之后,我发现这比抓住一个例外更好。

        7
  •  8
  •   Massimiliano    16 年前

    当我使用“as”时,99%的时间是当我不确定实际的对象类型时

    var x = obj as T;
    if(x != null){
     //x was type T!
    }
    

    我不想捕捉显式的强制转换异常,也不想进行两次强制转换,使用“is”:

    //I don't like this
    if(obj is T){
      var x = (T)obj; 
    }
    
        8
  •  8
  •   JulianR    16 年前

    这只是因为人们喜欢它的外观,它是非常可读的。

    让我们面对现实吧:C语言中的转换/转换操作符非常糟糕,可读性很强。如果C采用以下任意一种JavaScript语法,我希望它更好:

    object o = 1;
    int i = int(o);
    

    或定义 to 操作员,铸造当量 as :

    object o = 1;
    int i = o to int;
    
        9
  •  5
  •   Bob    16 年前

    人们喜欢 as 太多了,因为它让他们感觉安全,不受例外…就像盒子上的保证。一个家伙在盒子上放了一个精美的保证,因为他想让你感觉到里面的温暖和烘烤。你觉得你晚上把那个小盒子放在枕头下面,担保仙女可能会下来留下一角五分之一,对吗,泰德?

    回到主题…当使用直接广播时,有 可能性 对于无效的强制转换异常。所以人们申请 作为 作为一个整体解决方案来满足他们所有的铸造需求,因为 作为 (本身)永远不会抛出异常。但有趣的是,你举的例子 (x as T).SomeMethod(); 您正在将无效的强制转换异常与空引用异常进行交易。当您看到异常时,它会混淆真正的问题。

    我一般不使用 作为 太多。我更喜欢 is 测试,因为对我来说,它看起来更可读,更有意义,然后尝试强制转换并检查是否为空。

        10
  •  5
  •   Community Mohan Dere    8 年前

    这一定是我的 top peeves .

    斯特劳斯鲁的D&E和/或一些我现在找不到的博客文章讨论了 to 操作员,该操作员将处理 https://stackoverflow.com/users/73070/johannes-rossel (即,与 as 但与 DirectCast 语义)。

    之所以没有实现这一点,是因为强制转换会导致疼痛和难看,所以您会被推离使用它。

    遗憾的是,“聪明”的程序员(通常是书作者(juval lowy iirc))滥用 作为 以这种方式(C++不提供 作为 可能是因为这个原因)。

    即使是vb在使用统一的语法时也有更大的一致性,这迫使您选择 TryCast 定向铸造 下定决心 !

        11
  •  2
  •   Andrew Garrison    16 年前

    我相信 as 关键字可以被认为是 dynamic_cast 来自C++。

        12
  •  1
  •   Jla    16 年前

    它可能更受欢迎,因为没有技术原因,但只是因为它更容易阅读和更直观。(不说只想回答问题就更好了)

        13
  •  1
  •   Ruben Bartelink    16 年前

    使用“as”的一个原因:

    T t = obj as T;
     //some other thread changes obj to another type...
    if (t != null) action(t); //still works
    

    代替(错误代码):

    if (obj is T)
    {
         //bang, some other thread changes obj to another type...
         action((T)obj); //InvalidCastException
    }