【问题标题】:Throw/do-not-throw an exception based on a parameter - why is this not a good idea?根据参数抛出/不抛出异常 - 为什么这不是一个好主意?
【发布时间】:2009-03-09 00:49:29
【问题描述】:

我在 MSDN 中四处搜索,发现 this article 有一个有趣的建议:不要有可以根据某些选项抛出或不抛出异常的公共成员。

例如:

Uri ParseUri(string uriValue, bool throwOnError)

现在我当然可以看到,在 99% 的情况下,这将是可怕的,但偶尔使用它是否合理?

我见过的一种情况是在访问数据库或配置文件中的数据时使用“AllowEmpty”参数。例如:

object LoadConfigSetting(string key, bool allowEmpty);

在这种情况下,替代方法是返回 null。但是随后调用代码将充满空引用检查。 (如果您愿意的话,该方法还将排除实际允许 null 作为特定可配置值的能力)。

你的想法是什么?为什么这会是个大问题?

【问题讨论】:

    标签: c# exception parameters option


    【解决方案1】:

    我认为根据布尔值来决定是否投掷绝对是个坏主意。即因为它要求开发人员查看一段代码以了解 API 的功能知识以确定布尔值的含义。这本身就很糟糕,但是当它改变底层错误处理时,开发人员在阅读代码时很容易出错。

    在这种情况下有 2 个 API 会更好,更易读。

    Uri ParseUriOrThrow(string value);
    
    bool TryParseUri(string value, out Uri uri);
    

    在这种情况下,这些 API 的作用是 100% 清楚的。

    关于为什么布尔值作为参数不好的文章:http://blogs.msdn.com/jaredpar/archive/2007/01/23/boolean-parameters.aspx

    【讨论】:

      【解决方案2】:

      通常最好选择一种错误处理机制并坚持使用它。允许这种触发器代码并不能真正改善开发人员的生活。

      在上面的例子中,如果解析失败并且 throwOnError 为 false 会发生什么?现在用户必须猜测是否返回NULL,或者上帝知道......

      确实,作为更好的错误处理方法,异常和返回值之间存在着持续的争论,但我很确定对于保持一致并坚持您所做的任何选择已经达成共识。 API 不会让用户感到意外,错误处理应该是接口的一部分,并且与接口一样明确定义。

      【讨论】:

        【解决方案3】:

        从可读性的角度来看,这有点令人讨厌。开发人员倾向于期望每个方法都抛出异常,如果他们想忽略异常,他们会自己捕获它。使用“布尔标志”方法,每个方法都需要实现这种异常抑制语义。

        但是,我认为 MSDN 文章严格指的是“throwOnError”标志。在这些情况下,要么错误在方法本身内部被忽略(不好,因为它被隐藏了),要么返回某种空/错误对象(不好,因为你没有使用异常来处理错误,这是不一致的并且本身就是错误的-容易)。

        而你的例子对我来说似乎很好。异常表示方法未能履行其职责 - 没有返回值。然而,'allowEmpty' 标志改变了方法的语义 - 所以本来应该是一个异常('Empty value')的东西现在是可以预料的并且是合法的。另外,如果您抛出了异常,您将无法轻易返回配置数据。所以在这种情况下似乎没问题。

        【讨论】:

          【解决方案4】:

          在任何公共 API 中,有两种方法来检查错误情况确实是一个坏主意,因为如果发生错误,将会发生什么变得不明显。仅仅通过查看代码将无济于事。您必须了解标志参数的语义(没有什么可以阻止它成为表达式)。

          如果检查 null 不是一个选项,并且如果我需要从这个特定的故障中恢复,我更愿意创建一个特定的异常,以便以后可以捕获它并适当地处理它。在任何其他情况下,我都会抛出一般异常。

          【讨论】:

            【解决方案5】:

            与此相符的另一个示例是在某些值类型上设置 TryParse 方法

            bool DateTime.TryParse(string text, out DateTime)

            【讨论】:

              【解决方案6】:

              拥有一个 donTThrowException 参数会破坏整个异常点(在任何语言中)。如果调用代码想要有:

              public static void Main()
              {
                      FileStream myFile = File.Open("NonExistent.txt", FileMode.Open, FileAccess.Read);
              }
              

              欢迎他们(C# 甚至没有检查异常)。在 Java 中,同样的事情可以通过以下方式完成:

              public static void main(String[] args) throws FileNotFoundException
              {
                      FileInputStream fs = new FileInputStream("NonExistent.txt");
              }
              

              无论哪种方式,决定如何处理(或不处理)异常是调用者的工作,而不是被调用者的工作。

              【讨论】:

                【解决方案7】:

                在链接到的文章中有一条注释,异常不应用于控制流 - 这似乎隐含在示例问题中。异常应该反映方法级别的失败。有一个可以抛出错误的签名似乎没有经过深思熟虑的设计。

                Jeffrey Richters 通过 C# 的书 CLR 指出 - “当方法无法完成其名称所指示的任务时,您应该抛出异常”。

                他的书还指出了一个很常见的错误。人们倾向于编写代码来捕获所有内容(他的话“一个普遍存在的错误,即没有接受过正确使用异常的适当培训的开发人员往往会过于频繁且不正确地使用 catch 块。当您捕获异常时,您是在说明你预料到了这个异常,你理解它发生的原因,并且你知道如何处理它。”)

                这让我尝试为我可以预期并且可以在我的逻辑中处理的异常编写代码,否则它应该是一个错误。

                验证您的参数并防止异常,并且只捕获您可以处理的内容。

                【讨论】:

                • 我认为你误解了这个场景。我们可以选择抛出异常(这将停止程序或其他),或者不抛出异常(即返回 null 或什么都不做)。在这两种情况下都不会抛出异常然后被捕获。
                【解决方案8】:

                我认为拥有一个参数来指示失败是否应该导致异常或简单地返回错误指示通常很有用,因为这样的参数可以很容易地从外部例程传递到内部例程。考虑类似的事情:

                Byte[] ReadPacket(bool DontThrowIfNone) // 记录为如果没有则返回 null { int len = ReadByte(DontThrowIfNone); // 记录为如果没有则返回 -1 如果 (len

                如果在读取数据时发生类似 TimeoutException 的情况会导致异常,则应在 ReadByte() 或 ReadMultiBytesbytes() 中抛出此类异常。但是,如果应将这种数据缺失视为正常现象,则 ReadByte() 或 ReadMultiBytesbytes() 例程不应引发异常。如果简单地使用 do/try 模式,则 ReadPacket 和 TryReadPacket 例程需要具有几乎相同的代码,但其中一个使用 Read* 方法,另一个使用 TryRead* 方法。恶心。

                使用枚举而不是布尔值可能会更好。

                【讨论】:

                  猜你喜欢
                  • 2014-07-09
                  • 2017-07-03
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-01-04
                  • 2010-12-09
                  • 1970-01-01
                  • 2014-04-22
                  相关资源
                  最近更新 更多