鉴于吃异常总是很糟糕juju并重新抛出异常会丢失调用堆栈,重新考虑以下因素的正确方法是什么?
吃异常:
try
{
… do something meaningful
}
catch(SomeException ex)
{
// eat exception
}
答案 0 :(得分:29)
try
{
...
}
catch(SomeException e)
{
//Do whatever is needed with e
throw; //This rethrows and preserves call stack.
}
答案 1 :(得分:7)
捕获并处理特定类型的异常。好的做法是不只捕获System.Exception。一个健壮的例程将强烈地键入它知道如何处理的异常。
不应将异常用于控制流,但通常需要根据异常类型采取特定的展开过程。
根据具体类型,您可能会也可能不会选择重新抛出它。例如,将ASP解析异常抛出到使用导致异常的代码的错误页面将导致无限循环。
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
}
答案 2 :(得分:5)
大多数人认为吃/抑制异常是完全邪恶的,特别是对于所有人而言。 (具有讽刺意味的是,他们使用了所有的回应“不要使用全能,它是邪恶的”:-)。我不明白人们谴责这种观点的宗教热情,因为如果使用明智,这种方法没有任何问题。
在我的书中,最糟糕的情况是我的程序灾难性退出 - >这会造成一个非常不满意的客户,导致数据丢失。每次都会出现未处理的异常。因此,如果异常被抑制,则无法处理异常在统计上更危险可能发生的任何不稳定风险。鉴于此,我们可以合理地做些什么来防止未处理的异常发生是一件好事。
许多人似乎忘记了catch alls经常可以正确处理任何异常,即使他们不知道异常的详细信息。我的意思是他们可以保证程序状态保持稳定,程序继续在其设计参数范围内运行。或者甚至可能存在副作用,例如用户发现按钮无响应,但他们仍然不会丢失任何数据(即优雅降级比致命崩溃更好)。例如,有时您希望在成功时返回一个值,如果因任何原因失败,则返回默认值。设计代码的一部分是知道何时向用户报告错误以及何时代表他们修复问题,以便他们的程序“正常工作”。在这种情况下,精心设计的全能通常是工作的正确工具。
例外令我担心。从根本上说,如果我不处理它,可以保证程序崩溃。如果我只为我期望的异常添加特定的异常处理,那么我的程序本身就很脆弱。考虑一下它是多么容易被打破:
支持“压制是邪恶的”观点的观点是,所有异常都代表不稳定或错误 - 但在许多情况下,程序员使用异常只返回状态信息。例如,FileNotFound。编写文件I / O代码的程序员已经代表我决定丢失文件是致命错误。它可能是。我应该抓住这个并确定它实际上是一种常见的,完全正常的或预期的情况。在很多时候,抑制异常是必要的,只需阻止别人的“决定”取出我的申请。简单地忽略错误返回码的旧方法并不总是坏事,特别是考虑到捕获和抑制无数“状态”异常所需的努力量。
默默地吃/禁止异常的诀窍就是确保这是正确处理它们的方法。 (在许多情况下,它不是)。所以可能没有必要重构你的示例代码 - 这可能不是坏事juju。
答案 3 :(得分:3)
这一切都取决于代码所在的位置。
在系统的深处?如果是这种情况,那么我会在整个产品中收集某种形式的标准错误处理,如果不是这样的话。
如果它在演示方面,例如它对除了代码之外的任何人都没有价值,在这种情况下,可能需要将其他逻辑放在finally块中。
或者让它完全卷起来,如果你不打算在捕获中做任何有用的事情,不要把它包裹在试试中。
… do something meaningful
答案 4 :(得分:2)
您的代码可以像这样重写(吃异常)
try
{
… do something meaningful
}
catch
{
// eat exception
}
但我不明白你想通过重构做什么!!
编辑:
使用throw;
重新投掷并不总是有效。阅读本文 - >
http://weblogs.asp.net/fmarguerie/archive/2008/01/02/rethrowing-exceptions-and-preserving-the-full-call-stack-trace.aspx
答案 5 :(得分:2)
添加已提供的优秀评论。
有三种方法可以“重新抛出”异常:
catch (Exception ex)
{
throw;
}
上面保留了原始异常的调用堆栈。
catch (Exception ex)
{
throw ex;
}
以上操作原始异常链并开始新的异常链。
catch (Exception ex)
{
throw new MyException("blah", ex);
}
上面将原始异常添加到新链的InnerException中。这可能是两个世界中最好的,但哪一个是正确的,高度依赖于你需要的东西。
答案 6 :(得分:2)
一般来说,除非你能够真正处理它,否则抓住将军Exception
并不是一个好主意。我认为正确的答案是Tim和Joshua的答案的结合。如果您可以处理并保持良好状态的特定异常,例如FileNotFoundException
,您应该抓住它,处理它并继续前进,如下所示:
try
{
// do something meaningful
}
catch(FileNotFoundException)
{
MessageBox.Show("The file does not exist.");
}
如果您无法处理并保持良好状态,请不要抓住它。
但是,如果您需要进行任何清理,例如在异常气泡之前中止数据库事务,则需要捕获常规Exception
并重新抛出它的一种情况起来。我们可以通过扩展前面的例子来实现这一点:
try
{
BeginTransaction();
// do something meaningful
CommitTransaction();
}
catch(FileNotFoundException)
{
MessageBox.Show("The file does not exist.");
AbortTransaction();
}
catch(Exception)
{
AbortTransaction();
throw; // using "throw;" instead of "throw ex;" preserves
// the stack trace
}
答案 7 :(得分:1)
将其重构为:
// No try
{
… do something meaningful
}
// No catch
并在主循环中处理异常。
答案 8 :(得分:0)
如果catch()块只重新抛出异常并且没有进行任何真正的异常处理,那么你根本不需要try..catch。
答案 9 :(得分:0)
吃异常的部分问题在于它本身隐藏不清楚。所以...正确的重构问题不容易回答。但是,理想情况下,您将完全删除try ... catch子句;在大多数情况下,这是不必要的。
最佳做法是尽可能完全避免try...catch
;如果你必须处理异常,那么尽可能本地和特别地处理异常并且不要将它们传播到堆栈中;最后,包括一个全局未处理的异常处理程序,它执行相应的日志记录(如果需要,可能会提供重新启动应用程序)。
答案 10 :(得分:0)
除非catch
块实际上对异常做了某些事情(例如,将其记录到系统错误文件中),否则甚至不需要try
/ catch
块。
话虽如此,如果异常值得告知用户(例如,记录它),那么一定要使用catch
块来执行此操作。
忽略异常的一个特别糟糕的缺陷是某些(致命的)异常应该导致程序终止。此类异常(例如,无法加载类)会使程序处于不稳定状态,这只会导致执行中的灾难。在这些情况下,记录异常然后正常终止是唯一合理的事情。
答案 11 :(得分:0)
您可以重新抛出异常而不会丢失调用堆栈,只需重新抛出
catch(Exception e)
{
throw;
}
你为什么需要这个? 用法示例: 在你的应用程序的某个地方你有第三方代码并且你包装它,如果它抛出异常你抛出WrappingException。
当您执行其他一些代码时,您可能会从3rdparty或您自己的代码中获得异常,因此您可能需要:
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);
}
在这种情况下,不要使用MyAppLayer3Exception包装WrappingException。
因此,在您的应用程序的顶层,您可能会捕获所有异常,并且通过了解异常类型,您将知道它的来源!
希望它有所帮助。
答案 12 :(得分:0)
吃异常的特殊方式并不重要。绝不要以任何方式吃异常!
仅捕获预期会发生的异常以及您可以执行某些操作的异常。这方面的示例包括文件和网络IO,安全异常等。对于这些情况,您可以显示用户发生的事情的说明,有时您可以优雅地恢复。
不要捕获永远不会发生的异常。这些示例包括空引用异常,无效操作异常等。应编写代码以便永远不会发生这些异常,因此无需捕获它们。如果发生这些异常,那么修复错误。不要吞下例外。
可以记录所有异常,但这应该通过程序上的未处理异常处理程序和任何创建的线程来完成。这不是通过try / catch完成的。
答案 13 :(得分:0)
try
{
.....
}
catch (something)
{
// we can safely ignore ex becuase ....
}
答案 14 :(得分:0)
有时,如果您真的不想处理异常带来的额外责任,最好不要处理异常。例如,为什么不尝试使用NullReferenceException
来捕获if (yourObject != null)
{
... do something meaningful with yourObject ...
}
,为什么不尝试确保该对象存在?
if-then
最好保留异常来处理那些你无法控制的事情,例如突然断开连接,或者有可能杀死长时间运行的进程,例如数据导入。抛出异常时,无论原因如何,您的应用程序都已达到不稳定的程度。您可以通过清理混乱来捕获异常以将应用程序恢复到稳定点,例如处理丢失的连接并创建一个新连接或记录发生错误的行并前进到下一行。
我一直在处理过去15年的异常处理,从Delphi的前六个版本开始,直到(包括).NET 1.0-4.0。它是一个强大的工具,但它是一个经常被滥用的工具。我一直发现,在此期间,最有效的异常处理流程在try-catch
之前推迟到{{1}}。
答案 15 :(得分:0)
异常层次结构的一个主要问题是异常根据发生的情况进行分类,而不是基于系统状态。一些例外意味着“一个函数无法执行其任务,但它也不会干扰系统状态”。其他人的意思是“为你的生命奔跑!整个系统正在融化!”在许多情况下,对于一个可以处理被调用方法失败的例程来说,吞下前一类型的任何和所有异常都是完全正确的;在其他情况下,应该以指示可能的状态损坏的方式重新抛出此类异常(例如,因为重置系统状态所需的操作失败;即使执行该操作的尝试没有干扰任何事情,事实也是如此国家没有重置意味着它已经被破坏了。
将一个人自己的异常管理到这样的层次结构中是可能的,但我不知道处理其他异常的任何好方法。