使用未处理的异常而不是Contains()?

时间:2008-08-11 23:49:33

标签: c# .net error-handling

想象一下,您正在使用的对象具有与其关联的其他对象的集合,例如,WinForm上的Controls集合。您想要检查集合中的某个对象,但该集合没有Contains()方法。有几种方法可以解决这个问题。

  • 通过循环遍历集合中的所有项目来实现您自己的Contains()方法,以查看其中一个是否是您要查找的内容。这似乎是“最佳实践”方法。
  • 我最近遇到了一些代码,而不是循环,尝试访问try语句中的对象,如下所示:
try  
{  
    Object aObject = myCollection[myObject];  
}  
catch(Exception e)  
{  
    //if this is thrown, then the object doesn't exist in the collection
}

我的问题是你认为第二种选择的编程习惯是多么糟糕?为什么?它的性能与集合中的循环相比如何?

8 个答案:

答案 0 :(得分:4)

一般的经验法则是避免使用控制流的例外,除非触发异常的情况是“特殊的” - 例如,非常罕见!

如果这是正常而且经常发生的事情,绝对不应该作为例外处理。

由于所涉及的所有开销,异常非常非常缓慢,因此如果发生这种情况,也可能存在性能原因。

答案 1 :(得分:2)

我不得不说这是非常糟糕的做法。虽然有些人可能会高兴地说通过集合循环对抛出异常的效率较低,但抛出异常会产生开销。我还会质疑为什么你使用集合来按键访问项目时更适合使用字典或散列表。

然而,我对此代码的主要问题是,无论抛出的异常类型如何,您总会得到相同的结果。

例如,可能抛出异常,因为该对象在集合中不存在,或者因为集合本身为null或者因为您无法将myCollect [myObject]强制转换为aObject。

所有这些例外都会以同样的方式处理,这可能不是您的意图。

这些是关于何时何地通常被认为可以抛出异常的好文章:

我特别喜欢第二篇文章的引用:

  

例外情况很重要   只有在意外或者意外时才会抛出   发生无效活动阻止   一种完成正常的方法   功能。异常处理   引入了一个小的开销和降低   性能所以不应该用于   正常的程序流而不是   条件处理。它也可以   很难维护代码   在此滥用异常处理   方式。

答案 2 :(得分:0)

如果在编写代码时,您希望此对象位于集合中,然后在运行时发现它不是,我会称之为异常情况,并且使用异常是正确的。< / p>

但是,如果您只是测试一个对象的存在,并且发现它不存在,那么这并不例外。在这种情况下使用例外是不正确的。

运行时性能的分析取决于所使用的实际集合以及搜索它的方法。不过这应该不重要。不要让优化的错觉欺骗你写出令人困惑的代码。

答案 3 :(得分:0)

我不得不考虑更多我喜欢它...我的直觉是,呃,不是那么多......

编辑:Ryan Fox对特殊情况的评论是完美的,我同意

至于性能,它取决于集合上的索引器。 C#允许你覆盖索引器操作符,所以如果它像你要编写的contains方法一样执行for循环,那么它将同样慢(由于try / catch可能会慢几纳秒......但是没有担心,除非代码本身在一个巨大的循环中。)

如果索引器是O(1)(甚至是O(log(n))...或者比O(n)更快的任何东西),那么try / catch解决方案当然会更快。

另外,我假设索引器抛出异常,如果它返回null,你当然可以检查null并且不使用try / catch。

答案 4 :(得分:0)

通常,对程序流和逻辑使用异常处理是不好的做法。我个人认为后一种选择是不可接受的例外使用。鉴于目前常用语言的特性(例如Linq和C#中的lambdas),没有理由不编写自己的Contains()方法。

最后的想法是,现在大多数集合已经有了一个contains方法。所以我认为这在很大程度上是一个非问题。

答案 5 :(得分:0)

例外应该是例外。

类似于'因为数据库已经从它下面掉出来而丢失了集合'是特例

“钥匙不存在”之类的东西是字典的正常行为。

对于winforms控件集合的特定示例,Controls属性具有ContainsKey方法,这是您应该使用的方法。

没有ContainsValue,因为在处理字典/哈希表时,没有快速迭代整个集合,检查是否存在某些内容,所以你真的不鼓励这样做。

至于为什么例外情况应该是特例,这是两件事

  1. 指出您的代码尝试执行的操作。您希望尽可能地使您的代码与其尝试实现的代码匹配,因此它具有可读性和可维护性。异常处理增加了一堆额外的障碍,阻碍了这个目的

  2. 代码简洁。您希望您的代码以最直接的方式执行它所做的事情,因此它是可读和可维护的。同样,异常处理所增加的障碍也妨碍了这一点。

答案 6 :(得分:0)

看一下Krzystof的这篇博客文章:http://blogs.msdn.com/kcwalina/archive/2008/07/17/ExceptionalError.aspx

异常应该用于通信错误条件,但它们不应该用作控制逻辑(特别是当有更简单的方法来确定条件时,例如Contains)。

部分问题是异常虽然对 throw 而言并不昂贵,但 catch 的代价很高,所有异常都会在某些时候被捕获。

答案 7 :(得分:-2)

后者是可接受的解决方案。虽然我肯定会抓住特定的异常(ElementNotFound?),但是在这种情况下集合会抛出。

速度,这取决于常见情况。如果您更有可能找到元素,那么异常解决方案将更快。如果您更有可能失败,那么它将取决于集合的大小及其迭代速度。无论哪种方式,你都希望在正常使用之前测量它是否真的是一个瓶颈,然后再担心这样的速度。首先要明确,后一种解决方案比前者更清晰。