【问题标题】:Commenting try catch statements评论 try catch 语句
【发布时间】:2010-09-29 15:55:31
【问题描述】:

在 try-catch 语句中解释错误处理的正确位置是什么?似乎您可以将解释性 cmets 放在 try 块或 catch 块的开头。

// Possible comment location 1
try
{   
    // real code
}
// Possible comment location 2
catch
{
    // Possible comment location 3

    // Error handling code

}

【问题讨论】:

    标签: c# error-handling comments


    【解决方案1】:

    我通常会做以下事情。如果只处理一个异常,我通常不会打扰,因为它应该是自记录的。

    try
    {   
        real code // throws SomeException
        real code // throws SomeOtherException
    }
    catch(SomeException se)
    {
        // explain your error handling choice if it's not obvious
    }
    catch(SomeOtherException soe)
    {
        // explain your error handling choice if it's not obvious
    }
    

    【讨论】:

      【解决方案2】:

      “评论是谎言”。处理这些变量名和一般逻辑,这样你就可以避免它。如果你真的需要撒谎,那就在 catch 块内撒谎。

      【讨论】:

      • 变量名不能告诉你为什么会这样。
      • 在这种情况下,异常类名通常很清楚。如果您 gasp 捕获一般异常,您可能只需要注释。
      • Grauenwolf,国际海事组织,如果你做对了,就没有理由“为什么”。 “为什么”是针对蹩脚的黑客和变通方法。
      • Sarah,“为什么”开发人员以一种方式而不是另一种方式做某事是非常重要的。例如,为什么我对 P/Invoke 参数使用 byte 而不是 bool?因为 bool 不适用于 64 位 Windows。没有评论,你怎么知道我为什么这样做?
      • 你提出了一个很好的观点,RW,虽然罕见的事情确实需要解释我觉得很多时候评论这些事情并不允许开发人员通过实际阅读来了解他们需要学习的内容代码。我知道我们不可能知道所有事情,但我发现这种情况极为罕见。
      【解决方案3】:

      我认为这根本不重要。

      我认为在评论中要记住的重要一点是解决为什么代码是它的方式,而不是什么代码在做什么,首先。这并不是说您不应该在简明的评论中解释复杂的逻辑,而是说为什么要重要得多。

      【讨论】:

        【解决方案4】:

        只需设置代码以使其不需要额外的 cmets 怎么样?

        try
        { 
           performDifficultAct( parameter );
        }
        catch (ArgumentOutOfRangeException couldNotFindArgument)
        {
           // handle exception
        }
        catch (Exception otherUnknownException )
        {
           // handle exception
        }
        

        如果您可以使用变量和方法命名来显示正在发生的事情,则无需记录。如果您必须记录或引发异常,则无需记录 - 源代码中的记录消息无论如何都应该是不言自明的。唯一需要在代码中添加额外文档的情况是,当代码在做什么完全不明显时,或者您必须添加一个容易错过的陷阱或模棱两可的步骤,这将需要任何查看未来的代码。

        编辑:为了澄清一点,这里有更多关于我如何使用这些“catch”语句来为维护程序员和用户/支持/质量保证/使用该软件的任何其他人提供有用的信息。还说明了我绝对想在代码中添加额外的 cmets 的那种情况:

        public void PerformSomeActionOrOther(string parameter)
        {
          try
          { 
             // For some reason an eleven character string causes a bluescreen from Kernel32
             if (parameter.Length==11) parameter+=" ";
        
             performDifficultAct( parameter );
          }
          catch (ArgumentOutOfRangeException couldNotFindArgument)
          {
             this.Log.WriteLn("Argument out of range exception in ArbitraryClass.PerformSomeActionOrOther");
             this.Log.WriteLn(String.Format("Probable cause is that {0} is not in the array", parameter));
             this.Log.WriteLn(String.Format("Exception: {0}", couldNotFindArgument.Message));
          }
          catch (Exception otherUnknownException )
          {
             this.Log.WriteLn("Unexpected exception in ArbitraryClass.PerformSomeActionOrOther");
             this.Log.WriteLn(String.Format("Exception: {0}", otherUnknownException.Message));
             throw( otherUnknownException );
          }
        }
        

        【讨论】:

        • "couldNotFindArgument" 是不够的。我想看看“如果出现此错误,请检查表 FooConfiguration”。
        • 那是您在 catch 块中调用适当命名的方法的时候(如果代码太复杂而无法自我解释)。注释应该保留用于解释难以理解的代码块,如果难以理解,则需要对其进行重构。
        • 本案评论直接针对开发者,要求他做点什么。通过方法命名向开发人员创建一些间接消息有什么意义?
        • 如果一个代码块自己不能理解,那么方法太复杂了。注释往往是糟糕的开发人员使用的拐杖,因为他们不知道如何处理代码异味。至少,一种新方法是值得的,但有时重构需要更加重要。
        • 这与理解代码块或复杂代码无关。这是关于直接向维护开发人员传递明确的信息。评论是完美的。
        【解决方案5】:

        绝对不要评论它的顶部,因为除了“在此处启动异常处理块”之外,您还能说什么有用的?对 catch 语句的评论更好,但总的来说,你要说什么? “处理 NullPointerException”?

        如果你需要说你正在做一些令人兴奋的事情,我会去发表评论,比如链接到应用程序域异常。

        【讨论】:

          【解决方案6】:

          我认为写得好的 try/catch 应该简洁而具体。我同意@Jason 的观点,即 why 更重要,但同样重要的是,让 catch 中的代码尽可能简洁。

          如果您使用特定的异常来捕获它也会有所帮助。例如,如果您使用 Java,请尝试捕获 NullPointerException 而不是通用异常。这应该可以解释为什么存在 try catch 以及您正在做什么来解决它。

          【讨论】:

            【解决方案7】:

            只要您保持一致,位置就无关紧要。我个人的喜好如下:

            //comment 1: code does XYZ, can cause exceptions A, B, C
            try {
                //do something
            }
            //comment 2: exception A occurs when foo != bar
            catch (ExceptionA a) {
                //do something
            }
            //comment 3: exception B occurs when bar is null
            catch (ExceptionB b) {
                //do something
            }
            //comment 4: exception B occurs when foo is null
            catch (ExceptionC c) {
                //do something
            }
            

            【讨论】:

              【解决方案8】:

              我知道这不是您要寻找的答案,但请不要发表评论。如果你的代码不够清晰,无法在没有注释的情况下独立存在,那么你应该重构它直到它清晰为止。 Jeffrey Palermo 刚刚写了一个 blog post 来说明它的最佳状态。

              通常,cmets 倾向于记录:

              • 代码过于紧凑。看起来像这样的东西:++i?--g:h-i;
              • 需要总结的长代码块
              • 一次性或没有明确存在理由的代码

              请参阅下面的关于异常块的一些简单注释的简化示例,以及无需 cmets 的版本。

              bool retries = 0;
              while (retries < MAX_RETRIES)
              {
                  try
                  {
                      ... database access code
                      break;
                  }
                  // If under max retries, log and increment, otherwise rethrow
                  catch (SqlException e)
                  {
                      logger.LogWarning(e);
                      if (++retries >= MAX_RETRIES)
                      {
                          throw new MaxRetriesException(MAX_RETRIES, e);
                      }
                  }
                  // Can't retry.  Log error and rethrow.
                  catch (ApplicationException e)
                  {
                      logger.LogError(e);
                      throw;
                  }
              }
              

              虽然上述 cmets 促进了可重用性,但您基本上必须同时维护代码和 cmets。可以(并且更可取)对其进行重构,以便在没有 cmets 的情况下更清晰。

              bool retries = 0;
              while (canRetry(retries))
              {
                  try
                  {
                      ... database access code
                      break;
                  }
                  catch (SqlException e)
                  {
                      logger.LogWarning(e);
                      retries = incrementRetriesOrThrowIfMaxReached(retries, e);
                  }
                  catch (ApplicationException e)
                  {
                      logger.LogError(e);
                      throw;
                  }
              }
              
              ...
              
              private void incrementRetriesOrThrowIfMaxReached(int retries, Exception e)
              {
                  if (++retries >= MAX_RETRIES)
                      throw new MaxRetriesException(MAX_RETRIES, e);
              
                  return retries;
              }
              
              private bool canRetry(int retries)
              {
                  return retries < MAX_RETRIES;
              }
              

              后一个示例可能看起来像更多的代码以获得非常微妙的好处,但收益不能被夸大。代码同样易于理解,但您的好处是您不需要拥有一组单独的元数据 (cmets) 来解释代码。代码解释了自己。如果您的 catch 代码块太长并且需要注释来总结,请考虑将其重构为单独的方法以提高可读性。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-09-17
                • 1970-01-01
                • 2014-03-02
                相关资源
                最近更新 更多