题
在 try-catch 语句中解释错误处理的正确位置是什么?似乎您可以在 try 块或 catch 块的开头放置解释性注释。
// Possible comment location 1
try
{
// real code
}
// Possible comment location 2
catch
{
// Possible comment location 3
// Error handling code
}
解决方案
我通常会做以下事情。如果只处理一个例外,我通常不会打扰,因为它应该是自我记录的。
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
}
其他提示
“评论是谎言”。处理这些变量名称和一般逻辑,以便您可以避免它。如果你真的需要撒谎,可以在catch区内进行。
我觉得这根本不重要。
我认为通过评论记住的重要事项是解决为什么代码是这样的,而不是代码正在做什么,首先是。这并不是说你不应该在简明的评论中解释复杂的逻辑,但为什么这么重要。
如何设置代码以便不需要额外的注释?
try
{
performDifficultAct( parameter );
}
catch (ArgumentOutOfRangeException couldNotFindArgument)
{
// handle exception
}
catch (Exception otherUnknownException )
{
// handle exception
}
无需记录您是否可以使用变量和方法命名来显示正在发生的事情。无需记录是否必须记录或引发异常 - 源代码中的记录消息无论如何都应该是不言自明的。唯一一次你需要在你的代码中需要额外的文档时,代码正在做什么是完全不明显的,或者你需要添加一个容易错过的问题或模糊的步骤,需要解释任何人代码将来。
编辑:为了澄清一点,这里有更多关于我如何使用那些“捕获”的信息。声明,为维护程序员和用户/支持/ QA /使用该软件的任何其他人提供有用的信息。还说明了我绝对想在代码中添加额外注释的情况:
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 );
}
}
绝对不要对它的顶部进行评论,因为除了“在这里启动异常处理块”之外,你能说些什么呢?关于捕获声明的评论更好,但总的来说,你还会说些什么? “处理NullPointerException”?
我会去评论IFF你需要说你正在做一些令人兴奋的事情,比如链接到应用程序域异常。
我认为一个写得很好的try / catch应该简明扼要。我同意@Jason的说法为什么更为重要,但同样重要的是要尽可能简洁地将代码保持在内部。
如果您使用了特定的异常,它也会有所帮助。例如,如果您使用Java,请尝试捕获NullPointerException而不是通用Exception。这可以解释为什么try catch存在以及你正在做什么来解决它。
只要您保持一致,位置无关紧要。我个人的偏好如下:
//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
}
我知道这不是您要寻找的答案,但请不要发表评论。如果您的代码不够清晰,无法在没有注释的情况下独立存在,那么您应该重构它,直到它变得清晰为止。 杰弗里·巴勒姆o刚刚写了一个 博客文章 这句话说得最好。
通常,评论倾向于记录以下任一内容:
- 代码太紧凑。事情看起来像这样:
++i?--g:h-i;
- 需要总结的长代码块
- 代码要么是一次性的,要么没有明确的存在理由
请参阅下面的简化示例,了解对异常块进行一些简单注释的示例,以及无需注释的版本。
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;
}
}
虽然上述注释促进了可重用性,但您本质上必须同时维护代码和注释。可以(并且最好)对其进行重构,以便在没有注释的情况下更清晰。
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;
}
后一个示例可能看起来需要更多代码才能获得非常微妙的好处,但其收益怎么强调都不为过。代码同样易于理解,但您的好处是不需要一组单独的元数据(注释)来解释代码。代码本身就解释了。如果您的 catch 代码块太长并且需要注释来总结,请考虑将其重构为单独的方法以提高可读性。