善用try catch块吗?


15

我总是觉得自己很努力……试图在try / catching和代码之间保持正确的平衡,而这些代码又不会变成令人讨厌的选项卡,方括号和异常,就像火薯一样被扔回到调用堆栈中。例如,我有一个正在开发的使用SQLite的应用程序。我有一个抽象SQLite调用的数据库接口,以及一个接受要进出数据库的事物的模型...因此,如果/当发生SQLite异常时,必须将其扔给模型(被称为模型) ),谁必须将其传递给调用AddRecord / DeleteRecord / whatever ...  

我喜欢异常,而不是返回错误代码,因为错误代码可以忽略,遗忘等,而本质上必须处理异常(允许,我可以立即捕获并继续前进...)肯定有比我现在要做的更好的方法。

编辑:  我应该用一些不同的措辞。我知道要重新投掷不同类型的球,我的措辞很差,这是我自己的错。我的问题是……这样做时如何最好地保持代码干净?过了一会儿,它才开始让我感到非常混乱。


哪种编程语言?
2011年

2
目前使用C#,但我尝试进行一般性的思考。
trycatch 2011年

C#不会强制声明抛出的异常,这使得在合理的情况下更容易处理异常,并避免程序员被诱使不实际处理它们而捕获它们。C#的设计师Anders Hejlsberg在本文中针对受检查的异常提出了理由artima.com/intv/handcuffs.html
2011年

Answers:


14

即使您没有使用强类型语言,也可以从强类型方面来考虑它-如果您的方法无法返回您期望的类型,则它应该引发异常。

而且,不要将SQLException一直抛出到模型(或更糟的是,UI)中,而是应捕获每个已知层的异常,并用适合该层的异常对它们进行包装/更改/替换:

Layer      Handles Exception
----------------------------
UI         DataNotFoundException
Model      DatabaseRetrievalException
DAO        SQLException

这应该有助于限制您在每一层中寻找的异常数量,并帮助您维护有组织的异常系统。


我做了一些编辑,用措辞不佳的原始Q ..我的问题更多是关于如何在处理所有尝试和捕获等问题时保持代码干净。当尝试捕获块到处都开始感到非常混乱……
trycatch 2011年

1
@Ed难道您的UI或模型正在捕获“ SQLException”?对我来说,这不是很重要。我猜对每个人来说。
妮可(Nicole)

1
@Ed,在没有检查异常的语言中可能还可以,但是在有检查异常的语言中throws SQLException,使用不暗示甚至涉及SQL的方法真的很丑。如果您决定某些操作应该进入文件存储,会发生什么?现在,您必须声明throws SQLException, IOException,等等。它会失控。
Mike Daniels

1
@Ed对最终用户SQLException的意义不大,尤其是如果您的系统可以支持关系数据库以外的多种类型的持久性。作为gui程序员,我宁愿要处理DataNotFoundException而不是整套低级异常。通常,低级库的例外只是正常生活的一部分,在这种情况下,当其抽象级别与应用程序匹配时,它们将更易于处理。
Newtopian

1
@Newtopian-我从未说过将原始的Exception呈现给最终用户。对于最终用户而言,只需将一条简单的“程序已停止工作”消息记录下来,同时将Exception记录在有助于获取支持的地方。在调试模式下,显示异常很有用。除非您有针对此类Exception的特定处理程序,否则您不应该关心API可能引发的所有可能的Exception;只需让您的未处理异常捕获程序处理所有这一切即可。
艾德·詹姆斯

9

异常允许编写更简洁的代码,因为其中的大部分内容都处理正常情况,而异常情况可以在以后甚至在不同的上下文中进行处理。

处理(捕获)异常的规则是必须由可以实际对其进行处理的上下文来完成。但这有一个例外:

必须在模块边界(特别是层边界)处捕获异常,即使仅将它们封装起来,也将引发对调用者有意义的更高级别的异常。每个模块和层都必须隐藏其实现详细信息,即使有关异常(堆模块可能会抛出HeapFull,但不会抛出ArrayIndexOutOfBounds)。

在您的示例中,高层不可能对SQLite异常做任何事情(如果这样做,那么它与SQLite的耦合是如此之大,以至于您将无法将数据层切换到其他东西)。有很多可预见的原因导致诸如添加/删除/更新之类的操作失败,并且其中一些(并发事务中的不兼容更改)甚至在数据/持久层中也无法恢复(违反完整性规则,fi)。持久层应将异常转换为模型层术语中有意义的内容,以便高层可以决定是重试还是优雅地失败。


我同意,并编辑了我的问题以反映我措辞不好的地方。我的意思是,这更多地是一个问题,该问题是如何避免尝试和追赶陷入混乱。
trycatch 2011年

您可以图层或模块中应用@Ed James和我提到的原理。与其直接从各处调用SQLite,不如使用一些与SQLite对话并从异常中恢复或将其转换为更通用的方法/函数。如果一个事务涉及多个查询和一个更新,则您不需要处理每个查询中的每个可能的异常:单个外部try-catch可以转换异常,而内部的try-catch可以通过回滚来处理部分更新。您也可以将更新移至自己的功能,以简化异常处理。
2011年

1
这东西会更容易与代码示例,了解..
点击给予好评

从一开始,这可能是一个更适合stackoverflow的问题。
2011年

5

通常,您应该只捕获特定的异常(例如IOException),并且仅在捕获到异常后才有特定的事情要做。

否则,通常最好使异常冒泡到表面,以便可以暴露和处理异常。有些人称此为快速失败。

您应该在应用程序的根目录中具有某种处理程序,以捕获从下面冒出的未处理异常。这使您有机会以适当的方式呈现,报告或管理异常。

当您需要在分布式系统上引发异常并且客户端没有服务器端故障的定义时,包装异常非常有用。


捕获和沉默异常是一件可怕的事情。如果程序损坏,则应崩溃并显示异常和回溯。它应该混乱地记录事物,但弄乱数据。
S.Lott

1
@ S.Lott我同意不应使异常沉默,因为它们会发现异常令人讨厌,但仅使应用程序崩溃是有点极端。在许多情况下,有可能捕获异常并将系统重置为已知状态,在这种情况下,所有内容的全局处理程序都非常有用。但是,如果重置过程又以无法安全处理的方式依次失败,那么是的,让它崩溃比将人的头扎在沙子里要好得多。
Newtopian

2
同样,这完全取决于您要构建的内容,但是我通常不同意仅让它们冒出气泡,因为这会造成抽象泄漏。当我使用API​​时,我更希望看到它公开了与API模型一致的异常。我也讨厌惊喜,所以我讨厌当API只让与实现有关的异常在未经通知的情况下窥视。如果我不知道发生了什么事,我该如何反应!我发现自己经常使用更广泛的捕获网来防止意外使我的应用崩溃。
Newtopian

@Newtopian:“包装”异常和“重写”减少了抽象的泄漏。它们仍然适当地冒泡。“如果我不知道发生了什么事,我将如何反应!我发现自己经常使用更宽的捕捞网”,这是我们建议您停止做的事情。您不需要抓住一切。80%的时间,正确的事情是什么也不会抓住。20%的时间有有意义的回应。
S.Lott

1
@Newtopian,我认为我们需要区分通常由对象抛出的异常和应该包装的异常,以及由于对象代码中的错误而引起的异常,而不应该包装。
Winston Ewert

4

想象一下您正在编写一个堆栈类。您无需在类中放置任何异常处理代码,因此它可能会产生以下异常。

  1. ArrayIndexError-当用户尝试从空堆栈弹出时引发
  2. NullPtrException-由于实现中的错误导致尝试引用空引用而引发

一种简单的包装异常的方法可能决定将这两个异常包装在StackError异常类中。但是,这确实错过了包装异常的要点。如果对象抛出低级异常,则意味着该对象已损坏。但是,在一种情况下这是可以接受的:对象实际上是损坏的。

包装异常的要点是对象应为正常错误提供适当的异常。从空堆栈弹出时,堆栈应引发StackEmpty而不是ArrayIndexError。这样做的目的是不是为了避免引发其他异常,如果对象或代码被打破。

我们真正要避免的是捕获已通过高级对象传递的低级异常。从空堆栈弹出时,抛出ArrayIndexError的堆栈类是一个小问题。如果您确实捕获到该ArrayIndexError,那么我们将遇到一个严重的问题。低级错误的传播要比捕获它们严重得多。

将其带回到您的SQLException示例:为什么要获取SQLExceptions?原因之一是因为您传递了无效查询。但是,如果您的数据访问层正在生成错误查询,则该查询将损坏。它不应尝试在DataAccessFailure异常中重新设置其损坏。

但是,由于与数据库的连接丢失,也会出现SQLException。在这一点上,我的策略是在最后一道防线中捕获异常,并向用户报告数据库连接已丢失并关闭。由于应用程序无法访问数据库,因此实际上没有更多的事情可以做。

我不知道您的代码是什么样的。但是听起来您可能会盲目地将所有异常转换为更高级别的异常。您应该只在相对少数情况下这样做。多数较低级别的异常表示代码中的错误。捕获并重新包装它们会适得其反。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.