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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8348/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-07-31 17:04:43  来源:igfitidea点击:

Using unhandled exceptions instead of Contains()?

提问by goric

Imagine an object you are working with has a collection of other objects associated with it, for example, the Controls collection on a WinForm. You want to check for a certain object in the collection, but the collection doesn't have a Contains()method. There are several ways of dealing with this.

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

  • Implement your own Contains()method by looping through all items in the collection to see if one of them is what you are looking for. This seems to be the "best practice" approach.
  • I recently came across some code where instead of a loop, there was an attempt to access the object inside a try statement, as follows:
  • Contains()通过循环遍历集合中的所有项目来实现您自己的方法,以查看它们中的一个是否是您要查找的。这似乎是“最佳实践”方法。
  • 我最近遇到了一些代码,其中不是循环,而是尝试访问 try 语句中的对象,如下所示:
try  
{  
    Object aObject = myCollection[myObject];  
}  
catch(Exception e)  
{  
    //if this is thrown, then the object doesn't exist in the collection
}
try  
{  
    Object aObject = myCollection[myObject];  
}  
catch(Exception e)  
{  
    //if this is thrown, then the object doesn't exist in the collection
}

My question is how poor of a programming practice do you consider the second option be and why? How is the performance of it compared to a loop through the collection?

我的问题是你认为第二种选择有多糟糕,为什么?与遍历集合的循环相比,它的性能如何?

采纳答案by lomaxx

I would have to say that this is pretty bad practice. Whilst some people might be happy to say that looping through the collection is less efficient to throwing an exception, there is an overhead to throwing an exception. I would also question why you are using a collection to access an item by key when you would be better suited to using a dictionary or hashtable.

我不得不说这是非常糟糕的做法。虽然有些人可能会乐于说循环遍历集合抛出异常的效率较低,但抛出异常是有开销的。我还会质疑为什么当您更适合使用字典或哈希表时,为什么要使用集合来按键访问项目。

My main problem with this code however, is that regardless of the type of exception thrown, you are always going to be left with the same result.

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

For example, an exception could be thrown because the object doesn't exist in the collection, or because the collection itself is null or because you can't cast myCollect[myObject] to aObject.

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

All of these exceptions will get handled in the same way, which may not be your intention.

所有这些异常都将以相同的方式处理,这可能不是您的本意。

These are a couple of nice articles on when and where it is usally considered acceptable to throw exceptions:

这些是关于何时何地通常被认为可以接受抛出异常的几篇不错的文章:

I particularly like this quote from the second article:

我特别喜欢第二篇文章中的这句话:

It is important that exceptions are thrown only when an unexpected or invalid activity occurs that prevents a method from completing its normal function. Exception handling introduces a small overhead and lowers performance so should not be used for normal program flow instead of conditional processing. It can also be difficult to maintain code that misuses exception handling in this way.

重要的是,只有在发生阻止方法完成其正常功能的意外或无效活动时才会抛出异常。异常处理引入了少量开销并降低了性能,因此不应用于正常程序流而不是条件处理。维护以这种方式滥用异常处理的代码也很困难。

回答by Ryan Fox

If, while writing your code, you expect this object to be in the collection, and then during runtime you find that it isn't, I would call that an exceptional case, and it is proper to use an exception.

如果在编写代码时,您希望此对象在集合中,然后在运行时发现它不在集合中,我会将其称为例外情况,并且使用异常是合适的。

However, if you're simply testing for the existence of an object, and you find that it is not there, this is not exceptional. Using an exception in this case is not proper.

但是,如果您只是简单地测试一个对象是否存在,而您发现它不存在,这也不是例外。在这种情况下使用异常是不合适的。

The analysis of the runtime performance depends on the actual collection being used, and the method if searching for it. That shouldn't matter though. Don't let the illusion of optimization fool you into writing confusing code.

运行时性能的分析取决于正在使用的实际集合,以及搜索它的方法。不过那应该没关系。不要让优化的错觉欺骗您编写令人困惑的代码。

回答by Patrick

The latter is an acceptable solution. Although I would definitely catch on the specific exception (ElementNotFound?) that the collection throws in that case.

后者是可接受的解决方案。尽管我肯定会发现集合在这种情况下抛出的特定异常(ElementNotFound?)。

Speedwise, it depends on the common case. If you're more likely to find the element than not, the exception solution will be faster. If you're more likely to fail, then it would depend on size of the collection and its iteration speed. Either way, you'd want to measure against normal use to see if this is actually a bottle neck before worrying about speed like this. Go for clarity first, and the latter solution is far more clear than the former.

速度方面,这取决于常见情况。如果您更有可能找到该元素,则异常解决方案会更快。如果您更有可能失败,那么它将取决于集合的大小及其迭代速度。无论哪种方式,在担心这样的速度之前,您都希望对照正常使用情况进行衡量,看看这是否真的是瓶颈。首先要清楚,后者的解决方案比前者清晰得多。

回答by Mike Stone

I would have to think about it more as to how much I like it... my gut instinct is, eh, not so much...

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

EDIT: Ryan Fox's comments on the exceptional case is perfect, I concur

编辑:瑞恩福克斯对例外情况的评论是完美的,我同意

As for performance, it depends on the indexer on the collection. C# lets you override the indexer operator, so if it is doing a for loop like the contains method you would write, then it will be just as slow (with maybe a few nanoseconds slower due to the try/catch... but nothing to worry about unless that code itself is within a huge loop).

至于性能,它取决于集合上的索引器。C# 允许您覆盖索引器运算符,因此如果它正在执行类似于您将编写的 contains 方法的 for 循环,那么它会同样慢(由于 try/catch 可能会慢几纳秒......但没有什么可担心除非该代码本身在一个巨大的循环中)。

If the indexer is O(1) (or even O(log(n))... or anything faster than O(n)), then the try/catch solution would be faster of course.

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

Also, I am assuming the indexer is throwing the exception, if it is returning null, you could of course just check for null and not use the try/catch.

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

回答by OJ.

In general, using exception handling for program flow and logic is bad practice. I personally feel that the latter option is unacceptable use of exceptions. Given the features of languages commonly used these days (such as Linq and lambdas in C# for example) there's no reason not to write your own Contains() method.

一般来说,对程序流和逻辑使用异常处理是不好的做法。我个人觉得后一种选择是不能接受的异常使用。鉴于当今常用语言的特性(例如 C# 中的 Linq 和 lambdas),没有理由不编写自己的 Contains() 方法。

As a final thought, these days most collections dohave a contains method already. So I think for the most part this is a non-issue.

最后想一想,现在大多数集合确实已经有一个 contains 方法。所以我认为在大多数情况下这不是问题。

回答by Jeff Atwood

The general rule of thumb is to avoid using exceptions for control flow unless the circumstances that will trigger the exception are "exceptional" -- e.g., extremely rare!

一般的经验法则是避免在控制流中使用异常,除非触发异常的情况是“异常的”——例如,极其罕见!

If this is something that will happen normally and regularly it definitely should not be handled as an exception.

如果这是正常和定期发生的事情,则绝对不应将其作为例外处理。

Exceptions are very, very slow due to all the overhead involved, so there can be performance reasons as well, if it's happening often enough.

由于涉及的所有开销,异常非常非常慢,因此如果它经常发生,也可能有性能原因。

回答by Orion Edwards

Exceptions should be exceptional.

例外应该是例外。

Something like 'The collection is missing because the database has fallen out from underneath it' is exceptional

像“集合丢失,因为数据库从它下面掉出来”之类的东西是例外的

Something like 'the key is not present' is normal behaviour for a dictionary.

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

For your specific example of a winforms Control collection, the Controlsproperty has a ContainsKeymethod, which is what you're supposed to use.

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

There's no ContainsValuebecause when dealing with dictionaries/hashtables, there's no fast way short of iterating through the entire collection, of checking if something is present, so you're really discouraged from doing that.

没有,ContainsValue因为在处理字典/哈希表时,没有什么快速的方法可以遍历整个集合,检查是否存在某些内容,因此您真的不鼓励这样做。

As for WHY Exceptions should be exceptional, it's about 2 things

至于为什么例外应该是例外的,这大约是两件事

  1. Indicating what your code is trying to do. You want to have your code match what it is trying to achieve, as closely as possible, so it is readable and maintainable. Exception handling adds a bunch of extra cruft which gets in the way of this purpose

  2. Brevity of code. You want your code to do what it's doing in the most direct way, so it is readable and maintainable. Again, the cruft added by exception handling gets in the way of this.

  1. 指示您的代码正在尝试做什么。你想让你的代码尽可能地匹配它想要实现的目标,所以它是可读和可维护的。异常处理增加了一堆额外的麻烦,这妨碍了这个目的

  2. 代码简洁。你希望你的代码以最直接的方式做它正在做的事情,所以它是可读和可维护的。同样,异常处理所添加的杂物妨碍了这一点。

回答by Scott Dorman

Take a look at this blog post from Krzystof: http://blogs.msdn.com/kcwalina/archive/2008/07/17/ExceptionalError.aspx

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

Exceptions should be used for communicating error conditions, but they shouldn't be used as control logic (especially when there are far simpler ways to determine a condition, such as Contains).

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

Part of the issue is that exceptions, while not expensive to throware expensive to catchand all exceptions are caught at some point.

部分问题在于异常虽然抛出并不昂贵,但捕获起来却很昂贵,并且所有异常都会在某个时候被捕获。