【问题标题】:Is it bad programming style to have a single, maybe common, generic exception?有一个单一的、可能是常见的、通用的异常是不好的编程风格吗?
【发布时间】:2010-04-05 00:05:23
【问题描述】:

所以在我的程序中,我有一些部分使用了像这样的 try catch 块

try
{
  DirectoryInfo dirInfo = new DirectoryInfo(someString); 
 //I don't know if that directory exists
 //I don't know if that string is valid path string... it could be anything

 //Some operations here
}
catch(Exception iDontCareWhyItFailed)
{
  //Didn't work? great... we will say: somethings wrong, try again/next one
}

当然,我可能会检查字符串是否是有效路径(正则表达式),然后我会检查目录是否存在,然后我可以捕获各种异常以查看我的例程失败的原因并提供更多信息......但是在我的程序中,它并不是真的必要。现在我真的需要知道这是否可以接受,以及专业人士会怎么说/怎么想。非常感谢您的关注。

【问题讨论】:

  • 我希望你不介意我重新命名你的问题。我认为这更清楚。这个问题本身很好,写得很好——我投了赞成票。 :)
  • 哇,感谢您的回答,非常感谢您的建议。我只想说我知道如何以另一种方式做到这一点......干净的方式......正如丹布莱恩特所说的“这种懒惰的编码”,我理解这一点。我需要知道我的懒惰代码是否能经受住公众的批评。

标签: c# coding-style


【解决方案1】:
  • 编写“主线”代码来处理预期的情况。
  • 编写异常处理代码...等待它...处理异常情况。这就是为什么它被称为“异常处理代码”。 :-)

如果主线预期的日常情况是路径不存在,则编写检查路径是否存在的主线代码。如果意外的、奇怪的异常情况是文件存在但已被其他用户锁定,请编写一个异常处理程序来处理该异常。

【讨论】:

  • 如果文件被锁定是预期的主线情况怎么办?你如何编写检查这一点的主线代码?
  • @gabe:我不知道。我不是文件系统对象方面的专家。你应该把你的问题直接问到那个人。
  • 如果找不到专家怎么办?您是尝试自己(不恰当地)编写代码,还是只是让函数调用正确处理并抛出异常?
  • @gabe:你的问题是“你停止打你妻子了吗?”如果我回答不,那我还在打我的妻子;如果我回答是,那么我承认我曾经打过我的妻子。这个问题的正确答案既不是是也不是不是,而是拒绝这个问题。你问我“当有两种方法来编写误导性、不正确和不可维护的代码时,你会选择哪一种?”我都不选;我尽我最大的努力编写清晰、正确、可维护的代码。
  • 抱歉,我想我只是不明白您的建议如何适用于手头的问题。如果他询问解析字符串,您的建议将意味着“在您希望解析工作的地方使用Parse,在您不知道的地方使用TryParse”。由于没有可调用的IsPathValidTryCreateDirectory,也没有好的方法来编写它们,您的建议如何应用?
【解决方案2】:

您不应该将异常用于流控制,因为它会损害性能,并且异常不是为此目的而设计的。相反,您应该测试Exists 属性以检查目录是否存在

【讨论】:

  • “异常会损害性能”的想法是转移注意力。如果您正在编写对性能敏感的代码(并且您会知道是否如此),那么您可能会避免使用它们。但是在 99% 的情况下(就像上面的代码——你每秒检查多少个目录?)你最好尽可能写最简单、最清晰的代码。这通常意味着使用异常。
  • 可以在 Exists 返回 true 和您实际尝试访问文件/文件夹之间删除文件/文件夹。这不是理论上的,而是实际发生的。
  • @Chris B,我只是认为当有完全可用的替代方案涉及异常时,使用异常不是一个好习惯,特别是如果异常发生率比较高。让例外真正成为例外,而不仅仅是因为你懒得处理的事情。
  • @Chris B - 使用异常进行流控制不是编写最简单、最清晰的 IMO 代码,而且性能不佳。您应该尝试预测并避免抛出异常。
  • @Lucus - 这确实是可能的,我认为您关心这种类型的异常而不是吞下它。
【解决方案3】:

如果你真的不关心为什么它失败了,并且你的程序不能合理地做任何事情来恢复,那么做这样的事情是可以的;我宁愿维护这样的东西,也不愿使用 3 个 try/catch 块来做同样的事情但不添加任何附加值的代码。我确实喜欢传达它并不重要的异常的名称,这比捕获名为ex 的变量要好。

不过有一些建议:

  1. 如果您要“捕获并忽略”,请更明确地说明哪些异常类型可以忽略。如果你知道你可以忽略FileNotFoundException,或IOException 的任何类,那么就抓住它。

  2. 如果要捕获一般异常,请考虑在某处记录异常。如果您的 try 块中存在逻辑缺陷,您当前的方法可能是一场维护噩梦。例如,假设您针对数组索引编写了一个错误的代码......您当前的代码将吞下它,并且没有提供比“目录不存在”更重要的事情发生的迹象。

【讨论】:

    【解决方案4】:

    这实际上取决于您将如何处理任何这些错误情况。

    如果在任何情况下您要退出并仅打印异常,并且您的用户熟悉阅读和理解这种输出,那么这完全没问题,您会浪费时间和代码来处理它更具体地说。

    另一方面,如果您最终在异常处理代码中使用了一堆 if 测试来根据发生的异常执行不同的操作,那么这很清楚地表明您应该以不同的方式处理不同的异常。

    如果您的用户不是“技术人员”并且想要比异常转储更多有用的错误消息,那么您应该自己进行更多自定义错误处理。即使他们是技术人员,他们也可能会喜欢更清晰的错误消息。

    没有正确的答案。这实际上取决于您的目标用户体验。

    【讨论】:

      【解决方案5】:

      我不建议使用包罗万象的异常处理程序。相反,您应该查找DirectoryInfo 以及它可以抛出的异常类型。然后你应该在调用构造函数之前尽量避免,只捕获你预期的那些异常。所以我的代码看起来像:

      try
      {
          if ( string.IsNullOrEmpty( someString ) )
              //do something or throw an exception
          if ( someString.Length > 248 )
              //do something or throw an exception
      
          DirectoryInfo dirInfo = new DirectoryInfo(someString); 
      
          If ( !dirInfo.Exists )
              //do something or throw an exception
      
          //path exists and is valid
          //Some operations here
      }
      catch(SecurityException)
      {
        //Didn't work? great... we will say: somethings wrong, try again/next one
      }
      catch(ArgumentException)
      {
        //Didn't work? great... we will say: somethings wrong, try again/next one
      }
      

      【讨论】:

        【解决方案6】:

        您应该尽可能进行验证。例外是昂贵的,应尽可能避免。

        对路径和其他验证进行检查并不一定会消除对异常处理和尝试块的需要。异常总是发生(目录不存在,网络未连接)它是意外的、不可预见的异常,try/catch 块旨在处理。

        【讨论】:

          【解决方案7】:

          我也对这种懒惰的编码感到内疚,通常是在提供“热门”功能的快速周转时。问题是清理惰性代码的优先级很低,因为新堆栈不可避免地会出现“热”的特性请求,这意味着这种惰性代码会不断累积。这些懒惰的异常捕获块会导致吞下真正意想不到的问题,突然间你的应用程序状态以一种意想不到的方式发生了变化。我正在约束自己不要这样做,因为编写惰性代码就像使用信用卡一样。它让您现在就可以交付您以后必须交付的东西,但是当您需要偿还时,它是复合式的。

          【讨论】:

            【解决方案8】:

            即使您自己检查(它是有效路径、路径存在、您可以访问它、它没有被锁定等),您仍然需要进行异常处理。尽管看起来不太可能,但从您检查它们到您实际访问路径的时间,条件没有理由不会改变。

            只要一开始就需要异常处理,为什么要做冗余检查?无论如何,操作系统将不得不再次执行所有这些操作,而且您很可能无论如何都无法正确处理它们。

            不必担心异常的成本。与执行 I/O 的成本相比,异常的成本可以忽略不计。

            也就是说,不做任何事情就捕获所有异常并不是一个好习惯。正如赛斯所说,要么只捕获IOException,要么记录异常。如果你不这样做,那部分代码将很难调试。

            【讨论】:

              猜你喜欢
              • 2014-04-25
              • 1970-01-01
              • 2010-10-11
              • 1970-01-01
              • 2011-02-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多