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

时间:2009-03-20 15:31:20

标签: c# exception exception-handling try-catch unhandled-exception

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

或者至少,没有捕获异常?

异常会立即停止申请,除非他们处理得对吗?所以我想我问你是否想故意让你的应用程序死掉?

14 个答案:

答案 0 :(得分:15)

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

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

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

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

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

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

答案 1 :(得分:8)

我之前遇到过一个非常好的规则:

当某个方法无法按其名称所做的操作时抛出异常。

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

要遵循的另一条规则是:

除非您知道自己想要做什么,否则不要发现异常。

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

答案 2 :(得分:5)

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

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

所以,我建议你改为:“我该什么时候才能发现异常?”

答案 3 :(得分:4)

不确定。例如,如果您尝试将一些字节加载到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的构造函数仍会抛出此异常,您必须处理它。要么是这样,要么使用Charset.forName():)

答案 4 :(得分:3)

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

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

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

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

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

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

答案 5 :(得分:1)

  

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

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

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

  

你会不会遇到异常?

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

答案 6 :(得分:1)

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

通常的做法是,如果您将异常捕获到无法处理的级别,则“抛出/重新抛出”异常。但是,您几乎总是会在问题中添加上下文,至少在较高级别添加一些日志记录,以表明它已被捕获并重新投入。

例如

A调用B调用C(抛出异常)

B抓住/重新抛出

抓住了。

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

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

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

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

编辑

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

答案 7 :(得分:1)

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

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

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

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

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

其次,如果你知道它不会被捕获,你应该抛出异常吗?我认为这个问题误解了可重用方法的本质。方法的整个概念是它有一个“契约”:它接受某些参数并返回一定的值,而且在某些条件下它会抛出某些异常。这是合同 - 这取决于调用者他们用它做什么。对于某些调用者,异常A可能表示可恢复的条件。对于其他呼叫者,它可能表示存在错误。根据我上面所说的,应该很清楚,如果异常表明存在错误,就不能捕获

如果你想知道这对Microsoft Enterprise Library's Exception Handling Block意味着什么:是的,它很糟糕。他们会告诉您catch (Exception x),然后根据您的政策决定是否重新抛出;太晚了 - finally块已经被执行了。不要那样做。

答案 8 :(得分:0)

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

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

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

答案 9 :(得分:0)

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

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

答案 10 :(得分:0)

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

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

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

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

答案 11 :(得分:0)

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

那并且摆脱了你不想允许的或者看似“不可能”的可能性。能够捕捉所有异常并继续使用的应用程序只是一个被混乱所环绕的围墙花园。

答案 12 :(得分:0)

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

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

系统(尚)知道如何修复不一致并正常恢复

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

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

答案 13 :(得分:0)

如果应用程序代码不允许出现条件,库通常会根据防御性编程检查抛出异常。通常会编写应用程序代码,以便大多数这些无效条件永远不会出现,因此永远不会抛出异常,所以没有必要抓住它们。

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

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

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