抛出异常的意义是什么?

4

我正在用C#编写一个登录类,并且试图在空密码、密码字符不足等情况下认真地抛出异常。突然想到的问题是,我应该如何处理这些异常?它们是为了什么或谁而存在的?无论我是否处理异常,应用程序都将在那一点失败。异常是为其他开发人员还是客户端设计的呢?


1
可能是[异常或错误码]的重复问题。 (https://dev59.com/WnVC5IYBdhLWcg3wjx_u) - Joel Etherton
5
我不认为这是一个重复的问题。这个问题是“为什么要这样做?”而那个问题是“我应该如何做?”尽管非常相似。 - jcollum
11个回答

6

如果是您自己创建的异常,那么您不应该对其进行任何操作。

异常是您向使用者传达某些无法恢复的错误信息的方式。您可以让使用者有机会纠正问题、记录错误或将异常传递到更高层级,直到有用的操作可以执行为止。


2
这是一个奇怪的说法。很多人最终会编写类和客户端! - André Caron
6
尽管这是正确的,但你需要能够转换角色。当你编写一个方法时,你不是该方法的客户端。后来当你编写客户端代码时,你不需要担心方法的细节,只需要关注合同即可。 - Vincent Ramdhanie
@hoakey - 如果您能优雅地从异常中恢复,并且应该在某个地方记录,但应用程序可以继续运行。有些异常无法恢复,会导致应用程序停止。 - Justin Niessner
1
@Vincent:是的,我完全同意,但是单独看第一句话,它就显得非常奇怪! - André Caron
@Andre - 我重新措辞了第一句话,希望能更加自然些。 - Justin Niessner
显示剩余4条评论

6

异常用于提供有关特定故障原因的详细信息。如果您仅让代码自己失败,您将错失提供有关故障实际原因更丰富细节的机会。

您的用户不应该看到您添加到异常中的信息。相反,考虑添加一个全局异常处理程序,捕获您的详细异常,记录信息,然后向用户显示友好的错误消息。

异常不仅在日志中提供有关故障原因的详细信息(例如,在您的示例中密码为空),还提供调用堆栈信息,指示导致异常的调用链。在登录表单中,这不那么重要。但是,在多线程异步客户端/服务器应用程序中,这可能是至关重要的。

本文包含许多良好的准则:http://msdn.microsoft.com/en-us/library/ms229005.aspx


3

向更高一级的抽象层发出信号,表明存在问题,并改变其逻辑流以解决问题。


3
异常本身发生是为了防止未来出现更大的问题。如果你在知道发生不该发生的事情时默默无言地什么也不做,调用你代码的程序可能会认为用户的文件已经保存,而实际上并没有保存,这显然比能够告诉用户“我无法保存您的文件”要糟糕得多。
异常消息是为其他开发人员准备的。如果程序在开发过程中崩溃,开发人员应该能够查看堆栈跟踪,并更容易地找出为什么会发生不该发生的事情。理想情况下,你将能够以这样一种方式记录错误,以便开发人员甚至可以在生产环境中看到它们。

我个人更喜欢使用 On Error Resume Next... 一想到它,我就不寒而栗。有些错误的症状让我必须修复,这些错误距离引起错误的源头相差甚远,就像在现实世界中走了数英里的距离一样。 - ChaosPandion
@ChaosPandion:我表示同情。Fail-fast 是你的朋友。 - StriplingWarrior

2

异常通常表示方法的约定已被违反。该方法的客户端关心此异常并应适当处理。当约定被违反时,该方法本身通常无法恢复并且无法产生有意义的结果。异常表明没有有意义的结果即将来临。


+1:这可能是我听过的关于异常处理最明智的规则。然而,在登录上下文中使用纯错误代码存在安全问题。 - André Caron

1
简而言之,重点是指出本不应发生的事情确实发生了。

我经常在错误代码与异常讨论中听到这句话,但它从来没有像一个好的论点。"不应该发生"是有争议的。 - André Caron
抛出和处理异常的代价很高——一个设计良好的系统将在执行可能产生异常的操作之前验证条件是否满足。这并不是说异常永远不应该发生——但它们应该保留用于情况不正确或发生意外情况的情况。 - STW
在这种情况下,您可能不会为空密码抛出异常,因为这并不是一个真正的失败吗? - hoakey
@hoakey -- 该方法用于验证密码;因此,如果没有提供密码,则可以返回false(意味着“不合法”)或抛出ArgumentNullException(意味着“您没有给我要验证的密码”)。我倾向于后者;验证器的工作不是确保用户输入必填字段,因此抛出异常是完全可以接受的。但是,返回false也不是错误的。 - STW
@STW - 我慢慢地发现,在编程方面,可能会花费太多时间去思考替代方法,而不是继续进行编码工作。 - hoakey
@hoakey -- 绝对不仅仅是你。作为开发人员,能够思考不同的方法并权衡每种方法的优缺点是一项关键技能。在工程领域中没有银弹;总是以相同的方式做事几乎保证了你有时会做错事情。 - STW

0

所以,你写了一个很棒的程序。这个程序有一个可能的失败点。如果由于任何原因程序的某个部分失败了,你可能仍然希望继续运行程序的其余部分,并且要么显式地指出失败,记录它,或者只是继续运行。

我将使用伪代码语法,但你应该能够理解:

  var pw=$_POST['pw'];
    var un=$_POST['un'];
    try{
      $sql="select lastlogin,access from users where un=q(/'$un'/) and pw= q(/'md5($pw)'/)";
      $user=$db->getRow($sql);
      if(!$user) {
          //they don't exist
      }else{
        //process their login
      }
    }catch(Exception $e){
          //we has a Database error. either my query s really screwed up or the DB is down. let's log it and exit this stream; service 
           $mylogger->log("Error while logging in using module $MODULENAME$ ".implode("<br/>",(array)$e));

         exit;
    }

嗯,我没有进行任何调试...也不确定我的q(/''/)语法是否正确。我想现在可以在Oracle中做到这一点,但我确实忘记了。 - FatherStorm

0

异常是代码通知调用者某种失败的一种方式。调用代码可以根据需要进行处理,例如显示错误消息、抑制异常并优雅地降级等。


0
展示客户做得不好的优雅方式。

0
如果您的函数在无法登录时会抛出异常,那么调用您的函数的代码可以假设,如果您的函数返回,则已经登录。这将减轻代码处理“登录成功”场景所需的工作量,但需要更多处理“登录失败”场景的工作量。如果代码有时会在半预期的失败情况下使用(例如尝试使用一组凭据登录;如果不起作用,请尝试其他组),并且有时会在失败意味着无法恢复的情况下使用,可能有助于设置一个“错误时抛出”布尔标志,或者拥有单独的“登录”和“尝试登录”方法。

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