代码之家  ›  专栏  ›  技术社区  ›  Hemant

对何时抛出异常感到困惑

  •  7
  • Hemant  · 技术社区  · 15 年前

    我正在研究一个设计用于与外部设备通信(通过RS232串行通信)的库。我在考虑错误处理策略,异常似乎是正确的,也是行业标准的错误报告方式。

    所以我很少阅读关于异常的指导方针。 One 很明显我应该 担心性能问题:

    不要使用错误代码,因为担心异常可能会对性能产生负面影响。

    Other 告诉我在正常情况下不要抛出异常:

    不要将异常用于正常或预期错误,或用于正常控制流。

    我无法在正常/预期情况和其他情况之间划清界限。例如,在我的库中,操作可能会失败,因为:

    1. 设备没有响应。(未连接电缆,设备未打开,波特率错误)
    2. 操作请求被设备拒绝,因为它无法对请求进行身份验证。
    3. 通信失败。(有人被电缆绊倒,设备突然断电)。

    我可以把以上所有的问题都看作是预期中的问题,因为它们在实践中经常发生(事实上,许多营销傻瓜打电话给我是为了解决我的软件中的问题,结果却发现他们没有将电缆连接到他们的笔记本电脑上)。所以不应该抛出异常,因为否则应用程序程序员将不得不在很多地方捕获那些异常(我相信很多捕获块也不好)。

    另一方面,我也倾向于认为这些都是我在某种程度上需要向应用程序程序员报告的错误,异常似乎是实现这一点的方法。如果不使用异常,我需要使用一些错误代码或错误枚举来报告这些问题。(丑陋,我知道)。

    你认为我应该采取哪种方法?

    9 回复  |  直到 15 年前
        1
  •  5
  •   Ash    15 年前

    你正在开发一个 图书馆 ,其他应用程序将使用的组件。

    因此,在您提到的预期情况下,我肯定会使用异常来与调用应用程序通信,说明有问题。您应该为每个场景定义一个自定义异常,然后清楚地记录它们可能发生的时间。

    这样,应用程序客户机代码就可以决定如何最好地继续。只有客户机应用程序才能做出这个决定,并且清楚记录的异常对这一点有很大帮助。

    自定义异常最好的一点是,您可以提供与问题/异常相关的多个有意义/有用的数据片段。这些数据也很好地封装在一个对象中。将其与错误代码和返回值进行比较。

    性能可能是一个问题,但只有当异常在一个紧密的循环或其他一些高活动情况中抛出时才会出现。为了避免这种情况,您还可以应用.NET框架使用的模式,即提供Try….()方法(例如 TryParse() )返回布尔值,指示操作是成功还是失败。

    无论哪种方法,我都会在开始时使用自定义异常,然后进行性能测试,以实际查看库中是否有某些部分需要优化。

        2
  •  3
  •   avandeursen    15 年前

    我将在以下方法中使用例外(受合同设计的启发)

    • 如果可能,提供布尔值 检查功能告诉您 操作是否安全 应用
    • 对于给定的操作,请考虑 这样的检查功能 前提条件:如果条件成立, 操作可以安全完成,如果没有,您 引发异常。

    通过这种方式,如果API用户可以使用if-then-else结构对其键逻辑进行编码。

    如果出现意外情况(由于微妙的时间问题,例如),将抛出一个异常:开发人员可以捕获并处理该异常。但是注意:这不需要在调用方法的地方:在调用堆栈中,它可以更高/更早,在处理所有奇怪异常的中心位置。

    我已经在数百万行C程序的错误处理代码的自动分析上做了一些工作。这些都是基于编码标准,需要手写检查和传播错误代码。事实证明,开发人员不喜欢编写这样的代码,他们忘记了它,而且很容易在其中犯错误。事实上,我们发现每1000行C代码中有2个编码标准偏差(有人可能会说是2个错误)。

    总而言之:(1)我会使用布尔检查程序(2)在调用堆栈的较高位置可以捕获异常;(3)在实践中依赖错误代码是不安全的。

        3
  •  2
  •   Andy Shellam    15 年前

    例外情况正是为这种特殊情况而设计的。我试着这样想——如果你假设你所依赖的一切在大多数时候都是正常的,而方法X失败了,那么这是一个特殊的情况,因为它通常不会发生,你应该定义异常来捕捉这种情况。

    因此,在您的情况下,您假设设备已启动并运行。因此,在这种情况下,例外情况是设备不接受连接、拒绝连接、不接受您发送的数据或不接收它应该发送的数据等。如果您的设备通常一天关闭几次,那么您希望它们被关闭,因此请使用返回代码或“bool isdeviceon”();“连接前检查它的方法。

    如果是你 确实期待 在正常情况下发生,例如查询设备的功能,但您想要的功能不可用,然后使用返回代码或bool方法-例如,“bool doesdevicehave thiscapability();”不可用时不捕获异常。

    另一个例子(对于GUI应用程序)是用户输入。不要使用例外,因为您确实希望用户输入错误的内容——我们不是完美的。

    我遇到了大量的性能问题,因为在异常情况下使用异常。一个例子是当我每天处理2-3次2GB数据文件时。有些行的有效价格格式为0.00。有些没有。我用了一个formatException来捕获那些没有的。

    两年后,当我有机会得到一个性能分析器时,捕捉到异常的那条特定的线负责分析文件所用时间的80%。我将其改为使用int的typarse()方法,并获得了巨大的速度提升。

    对于您的示例,我可能会使用:

    1. 设备无响应-如果设备应24/7开启,则异常;如果常规关闭,则返回代码。
    2. 未经授权的操作-例外
    3. 通信失败-异常
        4
  •  1
  •   Mark Rushakoff    15 年前

    有了RS232,除非你启用了硬件握手(大多数时候,人们不这么做),否则你就不会看到更多的数据从线路中进入。除了没有东西被发送到电脑之外,你无法判断设备是否连上了。

    我将1和3一起归类为RS232时间错误,2可能归类为RS232身份验证错误。

    一般来说,TimeoutError表示远程设备已锁定或未连接。身份验证错误是一种协议错误,但与此稍有不同的是,通信正常,但远程设备“只是拒绝”创建与PC的连接。

    我认为将这些设置为异常是合理的:在正常操作期间,您不会期望出现超时错误,也不会期望出现身份验证错误。

        5
  •  1
  •   Craig Gidney Mihai    15 年前

    是否引发异常是函数类型的结果。

    • 如果函数返回一个x,而您未能确定有效的x,则引发异常。
    • 如果函数是一个操作(如connect),而您未能完成该操作,则引发异常。
    • 如果函数是Tryx变体,不要抛出异常。

    所以我想我是说你应该把问题从“我应该抛出异常吗?”“调用我的库的人想要什么方法?”但要注意的是,你抛出的异常应该是 明显的 基于您提供的方法。

        6
  •  1
  •   AMissico    15 年前

    正常或 预期误差,或正常流量 控制。

    在方法实现中,为了更改执行流、处理特殊逻辑、特殊情况或处理正常或预期的错误,避免有意引起异常。例如,应在以下函数中删除异常处理。(它正在处理正常或预期的错误,但是正如注释所说convert.toString不会真正失败。)由于在方法中“设置”异常处理所需的时间,性能受到了轻微的影响。它不是一个重要的点击,但是如果您在一个循环中调用这个函数,那么它可能会变得重要。如果这个方法在库中,那么让任何异常冒泡到库的用户那里。(自定义异常不同,请参阅ash的答案。)

    Public Function Nz(ByVal value As String, ByVal valueIfNothing As String) As String
        Try
            Dim sValue As String = System.Convert.ToString(value) 'using Convert.ToString on purpose
            If IsNothing(sValue) Then
                Return valueIfNothing 
            Else
                Return sValue
            End If
        Catch ex As Exception
            'Convert.ToString handles exceptions, but just in case...
            Debug.Fail("Nz() failed. Convert.ToString threw exception.")
            Return String.Empty
        End Try
    End Function
    

    下面是该方法的“更好”实现:

    Public Function Nz(ByVal value As String, ByVal valueIfNothing As String) As String
        Dim sResult As String = String.Empty
        Dim sValue As String = System.Convert.ToString(value) 'using Convert.ToString on purpose
        If IsNothing(sValue) Then
            sResult = valueIfNothing 
        Else
            sResult = sValue
        End If
        Return sResult
    End Function
    
        7
  •  1
  •   AMissico    15 年前

    不要使用错误代码,因为 担心例外情况可能会影响 表现不佳。

    避免将所有内容设计为返回带有“out”参数的“true/false”函数,只是为了避免使用异常时的“想象的”性能问题。

        8
  •  0
  •   Scott    15 年前

    不必在引发异常或返回错误代码之间进行选择。尝试在异常情况下返回异常对象。性能影响不是在创建异常,而是在抛出异常。然后调用方可以检查“空”返回值。返回的异常可以是函数返回,也可以是几个“out”参数之一。

        9
  •  0
  •   supercat    13 年前

    如果一个方法未能以调用者准备处理的方式执行其主函数,则该函数通常应通过返回值(或很少通过写入通过引用传递的变量)来指示这种失败。如果函数以调用方可能不准备处理的方式失败,则最好抛出异常。如果一些调用者准备处理故障,而其他调用者不准备,那么最好使用try/do模式。从本质上讲,我们提供了两种方法:剂量测定法和染色体测定法。Dosomething承诺它要么成功,要么抛出一个异常。Trydomeshing承诺,除非发生真正意外的事情,否则不会抛出异常;它将通过返回值指示“预期的”失败。