你有没有捕获exception,或抛出一个不会被捕获的exception?

我已经处理过我会抛出/重新抛出exception的实例,知道它周围的代码会捕获特定的exception。 但是,有没有时间你想要抛出exception,知道它不会被捕获?

或者至少,没有捕获exception?

除非处理权利,否则例外会立即停止申请? 所以我想我是否想要你故意让你的应用程序死掉?

如果您的应用程序主要由其他客户端使用而不是独立的,那么在出现您不知道如何(或不想)处理的情况时抛出exception通常是有意义的,并且没有明智的做法你可以从中恢复的方式。 客户端应该能够决定如何处理您可能抛出的任何exception。

另一方面,如果您的应用程序端点,抛出exception本质上就成为一种通知机制,以提醒人们某些事情已经发生了严重错误。 在这种情况下,您需要考虑以下几点:

  • 应用程序的持续运行有多重要? 这个错误真的无法恢复吗? 抛出exception并终止你的程序并不是你想要在航天飞机上做的事情。

  • 您是否使用例外作为真实日志的代理? 几乎没有理由这样做; 考虑一个真正的日志记录机制。 捕获exception并让记录器解决发生的事情。

  • 你想通过自己抛出exception来传达什么? 问问自己抛出新exception的价值是什么,并仔细考虑是否有更好的方法来做你想要的。

  • 不捕获exception可能会使资源处于不良状态。 如果您没有优雅地退出,通常不会为您清理任何事情。 如果你需要这样做,请确保你理解你在做什么 – 如果你不想抓住它,至少考虑一下try-finally块,这样你就可以做一些整理。

我前一段时间遇到过一个非常好的规则:

当方法无法按照其名称所做的那样执行时抛出exception。

这个想法是一个例外表明事情出了问题。 在实施方法时,您不应该知道它是否会被正确使用。 使用您的方法的代码是否捕获exception不是您的责任,而是使用您的方法的人的责任。

另一条遵循的规则是:

除非你知道你想用它做什么,否则不要捕获exception。

显然,你应该在try … finally块中包含清理代码,但是你不应该仅仅为了捕获它而捕获exception。 而且你永远不应该默默地吞下exception。 虽然有时您可能希望捕获所有exception(例如通过在C#中执行catch(Exception ex)),但这些exception并不常见,并且通常具有非常特定的技术原因。 例如,当您在.NET 2.0或更高版本中使用线程时,如果exception从您的线程中逃脱,则会导致整个应用程序域卸载。 但是,在这些情况下,您至少应将exception详细信息记录为错误,并在注释中提供说明。

通常,当然在应用程序的早期迭代中, 不要捕获exception。 通常情况下,从exception中恢复将需要某种业务规则,并且通常不会为您定义这些业务规则。 如果您“处理”exception而不是让应用程序死亡,那么您很可能会为您的客户创建业务规则。 不好。

为了捕获它而捕获每个exception的一般模式使我比我可以计算的更令人头疼。 通常会有人在整个应用程序中放置某种通用exception处理代码,这不可避免地会隐藏错误或创建一些不需要的行为。 (顺便说一下,捕捉然后不再重新抛出更糟糕。)

所以,我建议你问一下: “什么时候应该抓住一个例外?”

当然。 例如,如果您尝试将一些字节加载到Java中的字符串中:

 try { String myString = new String(byteArray, "UTF-8"); } catch (UnsupportedEncodingException e) { // Platform doesn't support UTF-8? What is this, 1991? throw new RuntimeExceptione(e); } 

在这种情况下,没有优雅的降级,平台根本无法支持所需的操作。 你可以在初始化时检查这个条件,但是String的构造函数仍会抛出这个exception,你必须处理它。 要么是,要么使用Charset.forName():)

这就是……它是关于“层”,或“封装”,或“低耦合”。 在代码库中的某个位置,您正在编写一种方法来执行某些操作。 说这是一种公共方法。 因此,它不应该假设调用者有太多或任何东西……相反,它应该只是做它应该做的工作,无论是谁调用它以及调用者所处的环境。

如果,由于某种原因,它无法完成其工作,那么它需要告诉来电者“对不起,我做不到,这就是为什么”。 例外是一种很好的机制,让它告诉来电者(不是唯一的机制,而是我在大多数情况下见过的最佳机制)。

所以,当你抛出exception时,你不知道它是否会被捕获……因为你暴露了一个公共方法,你不知道谁可以选择调用它以及为什么。

捕获exception是“上下文”的工作。 例如,假设您正在使用可能会抛出exception的公共方法编写库。 然后,假设您正在使用Windows窗体应用程序中的该库。 Windows窗体应用程序可能会捕获exception并向用户显示消息框。

但是稍后,您可以使用Windows服务中的相同库。 服务更有可能捕获exception,记录它,将错误返回给原始调用者,但继续运行以便它可以处理更多请求。

因此,例外就像呼叫者和提供者之间的合同协议。 提供者说,“我要么完成这项工作,要么告诉你为什么我不能做。你从那里做的是你自己的事。” 打电话的人说:“好吧,如果你不能完成这项工作,只要告诉我为什么,我会决定在这种情况下该做什么。”

但是,有没有时间你想要抛出exception,知道它不会被捕获?

我会说,如果你手动抛出exception,大多数时候你不知道它是否会被捕获。 如果你知道它会被捕获你可以自己处理它而不是首先抛出exception。

公平地说,我认为这部分取决于您正在进行的编程类型,有时同一个程序员最终会构建库和消耗所述库的代码。

你有没有遇到exception?

如果您没想到/不知道可能会抛出exception。 但是把它放在一边并假设你知道exception,有时你会在一个层面知道它,但知道下一层是更适合处理它的地方。

这取决于应用程序的类型。 即使在exception发生到执行上下文之后,Web应用程序仍可继续运行。

如果你在无法处理exception的情况下捕获exception,通常会抛出/重新抛出exception。 但是,您几乎总是会在问题中添加上下文,至少在较高级别添加一些日志记录,表示它已被捕获并重新投入。

例如

A调用B调用C(抛出exception)

B抓住/重新抛出

抓住了。

在这种情况下,您希望B添加一些日志记录,以便您可以区分B生成和抛出错误,以及C生成并抛出错误。 这将使您在以后更好地调试和修复问题。

一般来说,你几乎不会想要一个例外来杀死你的程序。 最好的做法是优雅地捕获除外和退出。 这允许您保存任何当前打开的信息并释放正在使用的资源,以免它们被破坏。 如果您打算退出,您可以创建自己的“核心转储”信息报告,其中包含您在发现致命exception时所执行的操作。

如果您让exception终止了您的流程,那么您将无法获得自定义的定制崩溃信息,并且您也在跳过向用户提供友好错误消息的部分,然后退出。

所以,我建议总是捕捉exception,并且从不自愿让他们在你的程序中乱跑。

编辑

如果您正在编写库,则必须提前选择函数是否会抛出exception,或者是exception安全的。 在这种情况下,有时你会抛出一个exception,并且不知道主叫方是否会抓住它。 但是在这种情况下,只要api声明函数可以抛出exception,捕获它不是你的责任。 (我正在寻找一个词,意思是’可能会抛出exception’……任何人都知道它是什么?它会让我整天烦恼。)

首先,绝对存在不捕捉exception的情况。

有时,exception有时会告诉您程序处于未知状态。 有许多例外情况,鉴于exception类型,这几乎是非常真实的。 NullReferenceException基本上告诉你“有一个bug”。 通过捕获这样的例外,您可以隐藏错误,这在短期内听起来不错,但从长远来看,您会更乐意解决它。 该产品可能不会崩溃,但它肯定不会有预期的行为。

但对于我们为自己创造的exception类型也是如此。 有时,exceptionA被抛出的事实应该是“不可能的” – 但它已经发生了,所以有一个错误。

此外,当您捕获exception时会发生一些非常重要的事情:将执行try块内的整个调用堆栈的finally块(以及它调用的任何内容)。 那些最终阻止做什么? 好吧,什么都好。 如果程序处于未知状态,我真的有意义 。 它们可以从磁盘中删除有价值的客户数据。 他们可以抛出更多例外。 它们可能会破坏内存中的数据,使得无法诊断出错误。

因此,当exception指示未知状态时,您不希望再运行任何代码,因此无论您做什么, 都不要捕获exception 。 让它飞过,你的程序将无害地终止,并且Windows错误报告将能够捕获程序的状态,就像最初检测到问题时一样。 如果捕获exception,将导致执行更多代码,这将进一步搞砸程序的状态。

其次,你是否应该抛出exception,知道它不会被抓住? 我认为这个问题误解了可重用方法的本质。 方法的整体思想是它有一个“契约”:它接受某些参数并返回一定的值,此外它还会在某些条件下抛出某些exception。 这是合同 – 这取决于调用者他们用它做什么。 对于某些调用者,exceptionA可能表示可恢复的条件。 对于其他呼叫者,它可能表示存在错误。 根据我上面所说的,应该很清楚,如果exception表明存在错误, 则不得捕获

如果你想知道这对微软企业库的exception处理块意味着什么:是的,它已经很糟糕了。 他们告诉你catch (Exception x) ,然后根据你的政策决定是否重新抛出; 为时已晚 – finally块已经执行了。 不要那样做。

您可能不希望最终用户可以看到任何未捕获的exception,但允许API(其他程序员)的客户决定如何处理exception通常是可以接受的。

例如,假设您正在设计Java类库。 您公开了一个接受String的公共方法。 在您的应用程序中,空输入值将导致错误。 不是自己处理错误,而是检查空值,然后抛出IllegalArgumentException是可以接受的。

当然,您必须记录您的方法在这种情况下抛出此exception。 此行为将成为方法合同的一部分。

这取决于你被“被抓住”的意思。 某些东西,某个地方最终会捕获exception,无论它是底层操作系统还是其他东西。

我们有一个工作流程系统,可以执行由各个工作组成的工作计划 每个作业都运行一个代码单元。 对于某些例外情况,我们不希望在代码中处理它们,而是将其抛出堆栈,以便外部工作流系统捕获它(这完全发生在thrower的进程之外)。

如果您正在编写整个应用程序,那么您的理由就是您自己的原因。 我可以想到一些情况,你可能想抛出exception并让应用程序死掉,但大多数都不是很好的理由。

最好的理由通常是在调试时。 我经常在调试时禁用exception,以便让我更好地了解出现故障的地方。 如果在带有调试器的计算机上运行它,也可以在调试器中打开抛出的exception中断。

另一个可能的原因是在抛出exception后继续没有意义或者会导致可能无法恢复的数据损坏或更糟(想想机器人用激光束,但是你应该确定你的应用程序处理这些情况IMO,崩溃程序只是懒惰的方式)。

如果您正在编写自己不会使用的API代码或框架代码,那么您不知道是否有人会捕获您的exception。

是的,这是我唯一机会打击使用服务/对象的开发人员告诉他们“你知道它是不是!!!!”。

那就是摆脱你不想要的或者看似“不可能”的可能性。 能够捕获所有exception并继续运行的应用程序只是一个被混乱所环绕的围墙花园。

如果我需要一个中等大小的系统,它以某种方式处理我认为是一致的方式的数据。

在某个地方,我发现应用程序的状态变得不一致。

系统(尚未)知道如何修复不一致性并优雅地恢复

然后,是的,我会尽可能详细地抛出exception并导致应用程序尽快死亡,以避免对数据造成进一步的伤害。 如果它可以恢复,重要的是不要通过无力地掩盖这个烂摊子来加剧这个问题。

后来,一旦导致不一致的事件链被更好地理解,我更高的设施可以捕获该exception,修复状态,并继续最小的中断。

如果应用程序代码不允许出现条件,则库通常会基于防御性编程检查抛出exception。 通常会编写应用程序代码,使得大多数这些无效条件永远不会出现,因此永远不会抛出exception,因此没有必要捕获它们。

取决于语言(我主要考虑的是C ++而不是C#,而不是很清楚差异是什么)实际抛出的未捕获exception的影响可能与exception之前的日期相同被发明了。 例如,C库中防御性编程的一个通用策略是立即使用错误消息终止程序。

不同之处在于,如果exception抛出确实可能(希望这将通过unit testing发现),通常可以相对容易地添加exception处理程序,以便以更具建设性的方式从问题中恢复。 您不必重写库,也不必在应用程序代码中添加复杂的检查,以确保在发出exception抛出调用之前不会出现这种情况。

我有一些从未被捕获的exception抛出。 它们都是出于防御目的,虽然未被捕获但对于确实发生的exception是不利的,但这只发生在开发和测试期间,因为到目前为止我在应用程序代码中未能考虑的错误条件。 当它发生时,修复很不方便 – 不需要进行大规模的重构,不需要应用程序代码大量复杂的错误条件检查,只需一个带有相对简单的恢复的catch子句或“对不起,戴夫,我怕我做不到。“ 没有失败整个应用程序。