为什么使用 catch (Exception) 是错误的?

3
我已经多次阅读到使用

标记来关闭段落标记是不必要的。
catch (Exception ex) 
{
    Logger.LogError(ex);
}

不使用重新抛出异常是错误的,因为您可能会隐藏代码中未知的异常。

然而,我正在编写一个WCF服务,并发现自己在多个地方这样做,以确保服务不会崩溃。(SOA规定客户端不应该知道或关心内部服务错误,因为它们不知道服务实现)

例如,我的服务从文件系统读取数据。由于文件系统是不可预测的,我捕获了来自读取代码的所有异常。这可能是由于坏数据、权限问题、缺少文件等等。客户端不关心,它只会收到一个“数据不可用”的响应,真正的原因记录在服务日志中。我也不关心,我只知道读取时出了问题,我不想崩溃。

现在我可以理解可能会引发与文件系统无关的异常。例如,也许我没有足够的内存,尝试创建读取缓冲区已经抛出了异常。然而事实仍然是,内存问题仍然与读取相关。我尝试读取但失败了。也许还有足够的内存运行其他服务。我应该重新抛出内存异常并崩溃服务吗,即使它对其他任何东西都不会造成问题?

我很赞赏只捕获你能够处理的异常的一般思想,但是如果你有一个独立的代码片段可能会失败而不影响其他任何内容,那么捕获由该代码生成的任何错误是可以的吗?这难道不与具有应用程序范围的异常处理程序没有什么不同吗?

2
你说“真正的原因已经记录在服务日志中” - 这正是问题所在 - 如果你捕获错误却没有采取任何措施,它将不会被记录在任何地方。 - Shadow The Spring Wizard
@ShadowWizard:不确定我们是否在交叉目的地交谈。我正在记录服务日志中的问题,因此已经被记录了。:-? - GazTheDestroyer
@CodeInChaos: 正如我所说,读取代码可能因为任何原因而失败,而不会影响服务的其余部分,而不仅仅是IO问题。 - GazTheDestroyer
如果您使用catch(Exception){},那么如何记录它呢? - CodesInChaos
1
@GazTheDestroyer 我已经更新了代码示例,以显示您正在调用一个方法,而不仅仅是在那里留下了一条注释。我不得不阅读整个 Q 才能看出它不是一条注释。 - DaveShaw
显示剩余2条评论
9个回答

2
如果您采用这种策略,部署团队将很难弄清楚客户端不能正常工作的原因。至少在某个地方记录一些日志。

我正在记录:“真正的原因已记录在服务日志中” - GazTheDestroyer
捕获器不是空的。抱歉,我的解释/示例不太好。 - GazTheDestroyer

2

主要问题之一是,你永远不会知道出了什么问题。错误不仅对客户/消费者隐藏,对作为服务开发人员的你自己也是如此。


2
如果磁盘存在权限问题,我不会说您的服务按预期工作。在我看来,“数据不可用”的服务返回甚至比“错误”更糟糕。
想象一下,您是该服务的用户。您调用它并返回“没有数据”。您知道您的调用是正确的,但由于您没有获得任何数据,您会认为问题出在您身上,然后回去调查。这样可能会花费很多小时。
哪个更好?将错误视为错误,还是欺骗您的用户?
更新
错误取决于什么并不重要。应该处理访问问题。有时失败的磁盘应该被镜像等等。SOA使您作为开发人员承担更多责任。隐藏错误并不能使它们消失。
SOA应传递错误。它可能不是详细的错误,但应足以让客户端了解服务器出现了问题。基于此,客户端可以稍后再试,或仅记录错误或通知服务台可能需要采取手动操作。
如果您返回“无数据”,您将不会给用户机会根据他们自己的意愿处理错误。
更新2
我在顶层使用捕获所有异常。我将异常记录到事件日志中(正在监视该日志)。
您最初的问题没有在catch中添加任何内容(现在已经添加)。只要记住监视该日志(以便您可以纠正这些错误),就可以了。

1
哪个更好?将错误视为错误,还是对用户撒谎?- 我喜欢这句话 - glosrob
但这并不是谎言,客户请求的数据确实无法获取。可能是因为数据不存在,也可能是因为数据已损坏,或者是因为服务在访问时抛出了其他异常。无论如何,这些数据对客户来说都是不可用的。不过我理解你的观点。也许我误解了SOA,我原以为它不鼓励向客户传递错误信息。 - GazTheDestroyer
那句话完全是错的:因为你没有处理错误,也不需要对用户撒谎。通过重新抛出错误,你给了用户一些他不知道如何处理的神秘信息。你让他感到困惑和不安全。这是错误的!通过捕获错误、记录它并告诉用户一个自定义消息,比如:“很抱歉,出现了问题,我们已经注意到了这个问题,并将尽快解决,如果您需要进一步支持,可以通过电话或电子邮件联系我们”,你正在以完全正确、用户友好的方式处理它。 - Andreas
@jgauffin:好的,谢谢,这有助于我对SOA的理解。然而,即使我将“错误”传回客户端而不是“无数据”,它并没有真正影响我的原始问题,即捕获所有异常而不是特定异常是否错误?肯定客户端想要一个“错误”,无论发生什么错误,而不仅仅是我能想到的异常吧? - GazTheDestroyer

1

这段代码完全没有问题。确保用户得到一个好的提示信息,比如“由于内部问题,数据不可用。问题已记录,将会处理。”然后大家都没问题了。

只有当你吞掉异常,让世界上没有人能够修复它时,才会出现问题。

异常总是需要处理的,可以通过编写特殊代码或修复导致异常的路径来解决。你不能总是预见所有错误,但你可以承诺一旦发现就立即修复它们。


0

我认为这个想法是为了防止遗漏错误。考虑一下:服务返回正常,但不如预期。你必须去搜索事件日志、文件日志等,以找到潜在的错误。如果你在那里放置一个跟踪写入,你可以确定可能的原因,但很可能是问题区域和时间戳与其他日志相关联的区域。

如果您使用.NET Trace,可以在代码中实现,并在需要时打开它。然后,您可以在不重新编译代码的情况下打开它进行调试。

WCF服务可以使用FaultException将异常传递回客户端。在构建WCF n-tier应用程序服务器时,我发现这非常有用。


0

在生产环境中,确保您的服务不会崩溃是值得称赞的。但在调试时,由于明显的原因,您需要让应用程序崩溃。

然而,从您的解释中,我得到的印象是,您捕获了您期望抛出的异常,并使用空的catch块捕获其余异常,这意味着您永远不会知道发生了什么。

将所有异常作为最后一道防线进行捕获是正确的,但请记得记录它们,因为它们与您预期的异常一样有趣。在捕获和记录所有与网络、I/O、安全、身份验证、超时、错误数据等相关的异常之后,请记录其余异常。


0
通常情况下,每个异常都发生在特定的情况下。为了调试目的,尽可能多地获取有关故障的信息非常有用,这样即将修复错误的人就知道在哪里查找并可以在最短时间内修复代码,从而为老板节省一些钱。
此外,抛出和捕获特定的异常将使您的代码更易读/理解和更易于维护。在任何角度上,捕获一般异常都会失败。因此,还要确保在抛出异常时,它已经得到了很好的记录(例如在XML注释中,以便调用者知道该方法可能会抛出特定的异常),并且具有清晰的名称,说明出了什么问题。同时,只包含与问题直接相关的信息(如ID等)在异常中。

0
在vb.net中,一个更好的方法是“Catch Ex As Exception When Not IsEvilException(Ex)”,其中“IsEvilException”是一个布尔函数,用于检查Ex是否类似于OutOfMemoryException、ExecutionEngineException等。捕获并重新抛出异常的语义与不捕获异常的语义略有不同;如果特定的“catch”语句除了按原样重新抛出异常之外什么也不做,最好不要首先捕获它。
由于C#不允许异常过滤,您可能会陷入几个不太好的选择:
1. 显式地捕获和重新抛出任何“邪恶”的异常类型,然后对其余部分使用一个通用的catch。 2. 捕获所有异常,将所有邪恶的异常“按原样”重新抛出,然后让您的逻辑处理其余部分。 3. 捕获所有异常,并让您的逻辑处理它们,而不考虑邪恶的异常,认为如果CPU着火,它会在数据被破坏之前引发足够多的其他异常来使程序崩溃。
Java和.net中异常处理的一个问题是,它紧密地绑定了三个实际上有些正交的概念:
什么条件或操作引发了异常? 异常是否需要采取行动? 异常是否应被视为已解决?
请注意,有时异常会涉及两种或更多类型的状态损坏(例如,试图从流中更新对象并且读取流时出现问题,导致流和正在更新的对象都处于错误状态)。处理任何一种情况的代码都应对异常进行操作,但在运行两种情况的代码之前,异常不应被视为已解决。不幸的是,没有标准的编码模式来处理这种行为。可以向任何自定义异常添加虚拟的只读“已解决”属性,并重新抛出任何这样的异常,如果“已解决”返回false,则不支持此属性的任何现有异常。

-1

如果有什么问题,那就是有问题。它应该崩溃。

如果代码中出现崩溃,你永远无法保证正确的操作。即使这意味着一个页面/表单在被访问时总是会崩溃,你也无法保证事情会继续正常运行,或者任何更改都能够被功能性地提交。

如果数据资源崩溃并返回一个条目为0的资源,当明显应该有结果时,用户将会感到非常困惑。用户将花费宝贵的时间来尝试找出自己做错了什么。

我唯一建议捕获所有异常(并记录它们,而不是忽略它们)的时候,是当运行的代码来自插件/用户编译的代码或某些奇怪的COM库,而你自己无法控制时。


3
你所说的本身就是错误的!如果某物确实存在问题,它绝对没有必要崩溃:必须修复错误!这只需要告知有关错误的人员即可,而记录日志完全可以胜任此任务。如果您让应用程序崩溃,那么您所做的一切就是将告知错误的负担放在用户身上,我相信任何用户都不愿承担这种负担!如果用户收到一个良好的问题通知而不是严重的崩溃,则用户将感到更加愉快。我最讨厌的就是完全失败的东西。 - Andreas
我故意说话刻薄,因为说出严厉的版本并根据应用程序进行修改比说出松散的版本更好。我的意思是你应该始终让当前异常被抛出。如何在绝对根级别处理它取决于程序员:显示一个漂亮的错误消息或显示一个自动报告工具;只是不要继续正常的页面/表单操作。只是你不应该吞掉异常发生的地方。但感谢你的投票,这会教会我在回答中更清楚明白。 - Stmated

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接