【问题标题】:How, exactly should I be using exceptions?我究竟应该如何使用异常?
【发布时间】:2011-07-17 16:30:05
【问题描述】:

对 C# 很陌生,我想知道我应该如何使用异常?我的意思是,不是机械水平,而是良好的实践水平。

例如,我将使用我的计算器进行标记化并转换为 RPN 并解决 RPN 中给出的问题。

在标记化步骤中,有各种无效输入,例如“7.7.8”或“^#”,我是否应该对未知符号和无效数字有单独的例外?说有一个异常,然后在其中包含一个包含错误类型的方法,将其提供给用户是错误的吗?

我真的找不到太多关于这种事情的材料,所以我想我会问比我更有经验的人。

-----编辑:

感谢大家的精彩回答,今天我学到了很多关于我的问题的知识,甚至更多。

【问题讨论】:

  • 异常是昂贵的,输入应该被验证。如果检测到无效输入,您需要在代码中处理它而不抛出异常。
  • @kzen 我不同意。如果提供了无效输入,您应该将 ArgumentException 抛回给调用者并让调用者处理它。异常可能很昂贵,但对于一个简单的计算器应用程序,为无效输入抛出异常是相当标准的。
  • @kzen - 取决于输入的来源。来自用户,是的,您应该优雅地验证和失败。从另一个代码块中,该代码被搞砸了,异常是通知开发人员的方式
  • 本例使用异常似乎大大简化了程序的流程,我用异常处理无效输入。昂贵在这里并不特别重要。
  • @Robert,这是对我原始声明的有效补充......

标签: c# .net


【解决方案1】:

这是 .Net 中异常处理最佳实践的一个很好的参考。

http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx

【讨论】:

  • 我不同意“对不应忽略的错误使用异常”。本指南的示例是使用异常处理控制流的经典示例。
【解决方案2】:

为程序员而非用户创建例外。想想什么对调用你的方法的程序员有用?

如果有专门的错误处理代码,他们应该考虑为特定的异常原因编写代码,然后提供特定类型的异常,以便他们可以有一个专门针对它的“catch”块。

如果他们应该编写的错误处理代码是针对各种异常原因的通用代码,则提供更通用的类型异常,以便他们只需针对该异常进行编码。

【讨论】:

    【解决方案3】:

    根据我的经验,您不应将异常用作代码逻辑或控制流的一部分。当出现真正的异常时,您应该使用它。在您的情况下,例如,当存在非法字符时,首先不应该存在的字符。但是,如果该函数正在验证字符串,则在发现非法字符时不应抛出异常,至少我是这样认为的。

    当找不到“应该”存在的数据库或文件时,我使用异常,当不应该存在的键/对值丢失时等等。

    【讨论】:

    • 我在很大程度上同意这个说法,但是如果无法准确修复无效输入,我认为方法中的无效输入是一种例外情况。如果您的方法因为提供了无效输入而无法给用户一个有效的答案,那么您应该抛出一个异常以便他们知道它,而不是仅仅返回 null 或其他同样没有意义的东西。
    • 异常是告诉用户无效输入的一种非常糟糕的方法,如果未处理,应用程序将崩溃。如果你处理它,那么为什么首先扔呢?只需验证输入并向用户显示一条好消息。
    • 但是如果您的应用程序被分成多个层,您不能只验证并向用户显示一条消息,因为查看输入的层不知道如何向用户显示该信息。另外,如果你的方法是public int Calculate(string value),你怎么表示返回的数字不正确,无效?
    • @KallDrexx 请参阅 Eric Lippert 关于 Vexing 异常的帖子:blogs.msdn.com/b/ericlippert/archive/2008/09/10/… Int.Parse 和 Int.TryParse 就是很好的例子。
    • 关键是如果你有一个例程 public SqlConnection(string connectionString) 如果字符串不是正确格式的连接字符串,那么函数应该抛出一个错误,但是如果例程是 public SqlConnection CheckConnectionString(string connectionString) ) 那么它绝对不应该抛出异常。当程序中没有预见到的事情发生时,应该抛出异常。它不应该用于通知用户事物的状态、字符串、数字、格式或其他任何东西。我认为这是一个很好的经验法则。
    【解决方案4】:

    Exception 应该是例外情况,用户输入不是其中一种情况。

    这个问题可能对你有帮助When to throw an Exception

    改用验证:Winform ui validation

    【讨论】:

      【解决方案5】:

      一般规则是KISS。如果需要,您应该只抛出/创建异常。如果没有人会抓住它,就没有必要创建一整套异常。

      在你的情况下,你可以

      1. 抛出一个 InvalidArgument 或创建您自己的指示它是无效的用户输入。
      2. 提供详细的错误消息说明问题所在。

      如果您发现自己正在捕获异常并解析消息并采取措施,那么是时候创建自定义异常/异常了。

      您可以创建具有“类型”字段的自定义异常,但任何依赖于异常的设计类型都是不好的。

      【讨论】:

        【解决方案6】:

        从前置条件和后置条件的角度考虑您的词法分析器。这里有两种设计词法分析器的方法:

        1. 词法分析器是一种接收格式良好的输入字符串并输出词法分析的设备。输入字符串要求格式正确;如果不是,则词法分析器失败并出现异常。

        2. 词法分析器是一种接受输入字符串并产生词法分析的设备。词法分析包括错误条件的识别和分析。

        知道您要传入的字符串是正确的,并且您希望在它不正确的特殊情况下出现异常吗?或者您不知道该字符串是否正确,并且您希望词法分析器确定这一点?

        在前一种情况下,抛出异常。在后一种情况下,将错误分析作为词法分析器输出的一部分。

        基本上我说的是do not produce vexing exceptions。令人烦恼的异常非常令人恼火。如果您的代码的调用者正在调用它以确定字符串是否正确,那么他们不希望以异常形式的信息,他们希望以数据对象的形式表示错误分析。如果代码的目的是产生错误分析,那么产生这样的分析作为方法的正常操作。不要将分析嵌入到异常中,并让某人捕获异常以获取它。

        【讨论】:

        • 你能抽出时间亲自回答这样的问题真是太好了,就像你是一个正常人一样!! :) 谢谢。
        • @Joel:谢谢你的客气话,但我向你保证,我是一个(相当)正常的人。
        • 让这个决定变得困难的是你需要知道调用代码对你的方法做了什么......
        【解决方案7】:

        您的所有解析异常至少应该共享一个公共基类,以便调用者可以捕获任何解析异常,而无需在 catch 子句中将它们全部列出(并且可能会忘记一个)。

        您可以继续对子类进行分类,但除非您需要这样做,否则我不会费心做出如此明确的区分。如果调用程序对所有解析错误采取相同的操作,那么它们都可以属于同一类,仅因显示消息而异。如果很明显某些错误需要以不同的方式处理,请创建一个子类。围绕用例进行设计。

        顺便说一句:我认为如果一个给定的方法不能实现它所承诺的,它应该抛出一个异常,从而保护开发人员在失败的情况下不会假设成功。决定做出哪些承诺是一个更大的设计问题,您可以查看现有的解析 API 以获得灵感。

        【讨论】:

          【解决方案8】:

          我喜欢判断异常是否合适的经验法则:如果一个函数承诺做某事但没有做,它应该抛出一个异常。一个简单的例子是Parse vs TryParse。请注意,Parse 函数承诺解析字符串,如果不能解析则抛出异常(例如,因为字符串格式错误)。 TryParse 承诺 try 进行解析,因此如果无法解析,它不会抛出异常。简而言之,如果你的函数做出了一个承诺并且没有实现它,你应该抛出一个异常。

          或者:除非您的函数的先决条件不满足,否则不要抛出异常。我将 Eric Lippert 的 Vexing Exceptions 解释为对哪些类型的先决条件不合适的讨论。

          【讨论】:

            猜你喜欢
            • 2019-08-21
            • 2011-12-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-01-22
            • 1970-01-01
            • 2011-09-17
            • 1970-01-01
            相关资源
            最近更新 更多