Eating Exceptions in c# om nom nom
考虑到吃异常总是不好的jujuju,重新抛出异常会丢失调用堆栈,那么重新考虑以下内容的正确方法是什么?
进食异常:
1 2 3 4 5 6 7 8 | try { … do something meaningful } catch(SomeException ex) { // eat exception } |
1 2 3 4 5 6 7 8 9 | try { ... } catch(SomeException e) { //Do whatever is needed with e throw; //This rethrows and preserves call stack. } |
捕获并处理特定类型的异常。好的实践不仅仅是捕获System.Exception。一个健壮的例程将强类型它知道如何处理的异常。
异常不应用于控制流,但通常需要根据异常类型采取特定的释放过程。
根据具体的类型,您可以选择或不选择重新显示它。例如,一个ASP解析异常被抛出到一个使用导致异常的代码的错误页,将导致无限循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | try { } catch( FileIOException ) { // unwind and re-throw as determined by the specific exception type } catch( UnauthorizedAccessException ) { // unwind and re-throw as determined by the specific exception type } catch( SomeOtherException ) { // unwind and re-throw as determined by the specific exception type } catch( Exception ) { // log and re-throw...add your own message, capture the call stack, etc. // throw original exception throw; // OR, throw your own custom exception that provides more specific detail and captures // the original exception as the inner exception throw new MyStronglyTypedException(); } finally { // always clean up } |
大多数人认为吃/抑制例外是非常邪恶的,尤其是在接球时。(具有讽刺意味的是,他们使用了"不要使用接球,这是邪恶的"的"接球"的所有响应":-)。我不理解人们对这一观点的宗教热情,因为如果使用得当,这种方法没有任何问题。
在我的书中,最坏的情况是,我的程序灾难性地退出了->这会导致一个非常不满意的客户出现完全的数据丢失情况。每次都有未处理的异常会导致这种情况。因此,在统计上,未能处理异常比抑制异常可能发生的不稳定风险更危险。鉴于此,我们可以合理地做任何事情来防止未处理的异常发生,这是一件好事。
许多人似乎忘记了catch all通常可以正确地处理任何异常,即使他们不知道异常的细节。我的意思是,它们可以保证程序状态保持稳定,并且程序在其设计参数范围内继续运行。或者甚至可能有副作用,例如用户发现按钮没有响应,但它们仍然不会丢失任何数据(即,优雅的降级比致命的崩溃更好)。例如,有时您希望在成功时返回一个值,如果由于任何原因失败,则返回默认值。设计代码的一部分是知道什么时候向用户报告错误,什么时候代表他们解决问题,这样他们的程序就"正常工作"。在这种情况下,设计良好的"一网打尽"通常是工作的正确工具。
例外让我担心。从根本上讲,如果我不处理程序崩溃,那么异常就是有保证的程序崩溃。如果我只为我期望的异常添加特定的异常处理,那么我的程序本身就是脆弱的。考虑它有多容易被破坏:
- 如果一个程序员忘记记录他们可能抛出的一个异常,我就不知道我需要捕获它,而且我的代码会有一个我不知道的漏洞。
- 如果有人更新了一个方法,使它抛出了一个新的异常类型,那么这个新的异常可能会在调用堆栈上激起涟漪,直到它碰到我的代码为止。但我的代码并不是用来处理异常的。别告诉我我打电话给的图书馆永远不会改变。
- 您专门处理的每个异常类型都是要测试的另一个代码路径。它极大地增加了测试的复杂性和/或处理代码的中断可能被忽视的风险。
支持"压制是邪恶的"观点的观点是,所有的异常都代表着不稳定或错误——但在许多情况下,程序员使用异常返回的仅仅是状态信息。例如,fileNotFound。编写文件I/O代码的程序员代表我决定丢失的文件是一个致命的错误。可能是这样。由我来理解这一点,并决定这实际上是一种常见的、完全正常的或预期的情况。很多时候,抑制异常对于简单地阻止别人的"决定"删除我的应用程序是必要的。简单地忽略错误返回代码的旧方法并不总是一件坏事,特别是考虑到捕获和抑制大量的"状态"异常所需的努力。
安静地吃/抑制异常的诀窍就是确保这是处理异常的正确方法。(在许多情况下,事实并非如此)。所以可能不需要重构您的示例代码——这可能并不坏。
这完全取决于代码所在的位置。
在系统的深处?如果是这样的话,那么我将收集一些标准的错误处理形式应该存在于整个产品中,如果不需要的话。
例如,如果它在表示端,它可能对代码以外的任何人都没有值,在这种情况下,可能需要将额外的逻辑放在finally块中。
或者让它一起上山,如果你无论如何都不想做任何有用的事情,就不要把它包在一个试抓中。
1 | … do something meaningful |
增加已经提供的优秀评论。
有三种方法可以"重新抛出"异常:
1 2 3 4 | catch (Exception ex) { throw; } |
上面保留了原始异常的调用堆栈。
1 2 3 4 | catch (Exception ex) { throw ex; } |
上面的内容将占用原始异常链并开始新的异常链。
1 2 3 4 |
上面将原始异常添加到新链的innerException中。这可能是两个世界中最好的,但哪一个是正确的,高度依赖于你需要什么。
总的来说,抓住将军的
1 2 3 4 5 6 7 8 | try { // do something meaningful } catch(FileNotFoundException) { MessageBox.Show("The file does not exist."); } |
如果你不能处理好这件事,留在一个好国家,那就不要在第一个位置。
但是,如果你有任何需要清理的信息,例如,在数据库交易之前,在例外情况下泡沫泡沫泡沫。我们可以通过扩展前面的例子来实现这一点。
ZZU1
您的代码可以这样重写(以消除异常)
1 2 3 4 5 6 7 8 | try { … do something meaningful } catch { // eat exception } |
但我不明白你想通过重构来做什么!!
编辑:
使用
Refactor it to:
1 2 3 4 5 | // No try { … do something meaningful } // No catch |
让例外在主要环路处理。
如果catch()块只返回异常,不进行任何真正的异常处理,那么根本不需要try..catch。
饮食异常的部分问题在于,他们隐藏的东西本质上是不清楚的。所以…正确重构的问题不容易回答。不过,理想情况下,您可以完全删除try…catch子句;在大多数情况下,这是不必要的。
最佳实践是尽可能完全避免
您可以在不丢失调用堆栈的情况下重新引发异常,只需重新引发
1 2 3 4 | catch(Exception e) { throw; } |
你为什么需要这个?使用实例:在你的应用程序中的某个地方,你有第三方代码,你包装它,如果它抛出异常,你就会抛出包装异常。
当您执行其他代码时,您可能会从第三方或自己的一方获得异常,因此您可能需要:
1 2 3 4 5 6 7 8 9 10 11 12 13 | try { //code that runs 3rd party //your code, but it may throw Null ref or any other exception } catch( WrappingException) { throw; } catch( Exception e) { throw new MyAppLayer3Exception("there was exception...", e); } |
在这种情况下,不使用myApplayer3异常包装包装包装异常。
因此,在应用程序的顶层,您可以捕获所有异常,通过了解异常的类型,您将知道它从何而来!
希望它有帮助。
在例外情况中的特殊方式并不重要。不要有任何例外!
唯一的例外是希望出现的,你能做些什么。Examples of this include file and network io,security exceptions,etc.for those cases you can show an explaination of what happened to the user,and sometimes you can gracefully recover.
Do not catch exceptions that should never occurred.Examples of these are null-reference exceptions,invalid operation exceptions,etc.The code should be written so that these exceptions will never happen,so there is no need to catch them.如果这些例外发生,那么就把错误固定下来。别掉以轻心
这是可以记录所有例外情况的,但这一例外情况应与无人驾驶的例外情况处理程序和产生的任何威胁一起进行。这不是一次尝试。
饮食例外并不总是"坏果汁"。这里没有魔法,只是写了你需要做的事情的代码。作为一个卫生问题,如果你有一个例外,而忽视它,你怎么做它。
1 2 3 4 5 6 7 8 | try { ..... } catch (something) { // we can safely ignore ex becuase .... } |
有时候,如果你真的不想与增添的责任做交易,而这是有例外的,那是最好的解决办法。比如说,在你尝试用它做任何事情之前,为什么不让它存在呢?
1 2 3 4 | if (yourObject != null) { ... do something meaningful with yourObject ... } |
例外是最好的保留来处理那些你实际上没有控制过的东西,例如连接的损耗,或者有可能杀死一个长的运行过程的东西,例如数据输入。当一个例外被击中时,考虑到原因,你的应用达到了一个不稳定点。你得到了一个例外,通过清理MES,E.G.处置失败的连接,创建一个新的或新的线路,在该线路上发现错误并向下一线路推进。
我在过去15年中以例外处理方式处理了这一问题,开始时有六种版本的德尔菲,Up to(and including).Net 1.0-4.0。这是一个强大的工具,但它是一个经常被过度使用的工具。在此期间,我发现最有效的例外处理程序是在
异常层次结构的一个主要问题是,异常是根据发生的情况而不是根据系统状态进行分类的。一些异常意味着"一个函数不能执行它的任务,但它也没有干扰系统状态"。其他人的意思是"为你的生命奔走!整个系统正在崩溃!"在许多情况下,对于能够处理被调用方法的失败的例程来说,吞咽前一类型的任何和所有异常都是完全合适的;在其他情况下,此类异常应以指示可能的状态损坏的方式重新引发(例如,因为在重置系统STA所必需的操作中出现故障即使执行该操作的尝试没有干扰任何内容,但状态未重置的事实意味着它已损坏)。
一个人可以将自己的异常管理到这样的层次结构中,但我不知道如何处理其他异常。
除非
也就是说,如果这个异常值得通知用户(例如,记录它),那么无论如何都要使用
忽略异常的一个特别坏的陷阱是某些(致命的)异常应该导致程序终止。此类异常(例如,未能加载类)会使程序处于不稳定状态,这只会在稍后的执行过程中导致灾难。在这些情况下,记录异常然后优雅地终止是唯一合理的做法。