何时重新抛出异常,何时返回FALSE?

15

我正在开发一个包装器(wrapper)以连接到一些特殊硬件的第三方函数库。因此,我想在一个MyHardwareObject中封装dll函数(bool Connect()void Disconnect()等) 并提供连接和断开连接方法。

dll中的Connect函数可能会引发一些特定的异常,例如当硬件不存在时。对于应用程序而言,连接方法失败的原因被认为是不重要的,因此异常中包含的额外信息并不需要。

处理这些异常的最佳方式是什么?应该返回false,还是在此处不处理异常,并在本来将处理连接方法返回 false 的级别上捕获它?

 bool MyHardwareObject.Connect()
{
    try
    {
        ThirdPartyLibrary.Connect();
    }
    catch (SomeException)
    {
        return false;
    }
    return true;
}
与其相反,
 bool MyHardwareObject.Connect() 
{
    ThirdPartyLibrary.Connect();
    return true;
}

(或者在第二种情况下更好的是void MyHardwareObject.Connect(),因为我们要么返回true,要么抛出异常?)

还有什么其它方案可供选择?最重要的是:为什么?

7个回答

9

在我看来,第一种情况肯定比第二种更好。您希望硬件库以一种方式包装第三方库,使您的用户不必知道其中的内容。
如果需要除了true或false之外的更多错误处理细节,您可以考虑重新抛出自己的异常,将第三方库中的异常转换为与您自己的代码更加一致的异常。


8
选择异常和返回码之间明智的选择真的很难。这取决于很多因素,处理错误的方式是最重要的因素 - 保持一致。
最近,我更倾向于“如果不值得,就不要抛出异常”。我们的异常做了很多工作:它们记录日志,创建堆栈跟踪等。如果我只想将一个字符串转换为整数,如果输入字符串格式不正确,则抛出异常可能不值得。另一方面,如果对象不能从正确的数据构建,则它们的构造函数会引发异常,我不希望它们存在于我的系统中。
如果代码库中没有给你任何提示该怎么做,我喜欢这个经验法则:想象一下,抛出异常会让你的计算机发出响亮的嘟声。只要你能忍受,抛出异常就可以了。

6
我建议将异常传递。如果不这样做,你可能会掩盖潜在的重要信息,这些信息在以后可能会有用。我知道现在你没有使用它,但是如果你决定根据错误类型来处理恢复,那么以后你就必须撤消所有已编写的代码来屏蔽异常。虽然我不同意@MadKeithV关于将其设置为true/false的观点,但我认为他在将异常包装在自己的异常中方面有一定的见解,这可能对你的应用程序具有更好的语义。

我并不是建议掩盖重要信息 - 而是认真考虑哪些信息是重要的需要传递,哪些信息应该由Connect安全地处理,无论是失败还是成功,而不是盲目地传递所有异常。所以我认为我们大多数人都同意。 - Joris Timmermans

6

阅读此文:http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html

简而言之:

调用者是否需要返回值?它能否恢复或错误是否意味着终结?如果只是小问题,使用返回代码。如果各种状态同样频繁发生(50%的时间失败,50%的时间成功),则使用返回代码,因为调用者必须处理该情况,而异常无法帮助。

您是否想传递额外的信息(例如第三方设置,以便稍后有机会调试发生了什么)?使用异常。


3

我建议使用第二个选项。这将由调用者负责处理异常并根据异常采取正确的操作。

假设Connect抛出AccountExpiredException、NetworkUnavailableException、InvalidCredentialsException等异常(这些只是例子),我可以提示用户“修复”异常(联系管理员,检查网络电缆,检查用户名/密码)。

我甚至建议删除返回值(指示器),只需使其void。看一下SqlConnection Open方法-它就像你的Connect-它不返回值,而是抛出异常。

像这样吞噬异常

 bool MyHardwareObject.Connect()
{
    try
    {
        ThirdPartyLibrary.Connect();
    }
    catch (SomeException)
    {
        return false;
    }
    return true;
}

我认为这不是一个好主意。我建议你实现IDisposable模式。仅供参考。


1

你的程序是否需要这个第三方库才能运行,或者它只是添加了一些额外的功能,虽然很好,但并不是必需的?此外,硬件故障的可能性有多大?最后,硬件会抛出多少种类型的异常?

如果这个库实际上对于你的程序来说并不是必需的,那么你肯定需要在某个时候捕获异常,但你可能希望在更高层次上进行。

如果硬件可以抛出多种类型的异常,那么你更可能想重新抛出异常,因为仅返回一个值将导致你丢失一些信息。然而,你确实说过客户并不关心失败的原因,只关心它是否失败,所以这不应该成为问题。

最后,如果硬件故障很常见,你可能更愿意返回一个错误代码(或在这种情况下返回false),而不是如果故障很常见。

我认为在你的情况下,这真的归结于第一个问题。如果你不真正需要这个库,就返回false,并确保非常清楚地记录操作可能因完全任意的原因而失败,并且他们需要检查返回值。如果你需要它工作,就重新抛出异常,让它传播到你可以优雅地退出的地方。


需要接口化这个硬件是应用的主要任务。请问我的理解是否正确:您不想使用返回值,因为某人可能仅通过调用 MyObject.Connect() 来使用它,而不检查返回值,例如if (!MyObject.Connect())... - Treb

0

我相信异常机制背后的原理是"快速失败"的理论。

如果连接不成功,你想让客户端应用程序"快速失败"吗?
如果是这样,请直接抛出异常或将异常封装在自己的应用异常中。

如果客户端忽略了该异常,他的应用程序将停止。

返回false意味着客户端可能会忽略问题,他的应用程序可能仍然会崩溃,但是"稍后"...


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