我经常(多年来)想知道实施以下最有意义的方式(对我来说这是一种悖论):
想象一个功能:
DoSomethingWith(value)
{
if (value == null) { // Robust: Check parameter(s) first
throw new ArgumentNullException(value);
}
// Some code ...
}
它被称为:
SomeFunction()
{
if (value == null) { // Fail early
InformUser();
return;
}
DoSomethingWith(value);
}
但是,为了捕获ArgumentNullException,我应该这样做:
SomeFunction()
{
if (value == null) { // Fail early
InformUser();
return;
}
try { // If throwing an Exception, why not *not* check for it (even if you checked already)?
DoSomethingWith(value);
} catch (ArgumentNullException) {
InformUser();
return;
}
}
或只是:
SomeFunction()
{
try { // No fail early anymore IMHO, because you could fail before calling DoSomethingWith(value)
DoSomethingWith(value);
} catch (ArgumentNullException) {
InformUser();
return;
}
}
答案 0 :(得分:0)
这是一个非常普遍的问题,正确的解决方案取决于具体的代码和架构。
通常涉及错误处理
主要关注点是在可以处理它的级别上捕获异常。
在正确的位置处理异常会使代码变得健壮,因此异常不会使应用程序失败并且可以相应地处理异常。
早期失败使应用程序健壮,因为这有助于避免不一致的状态。
这也意味着在执行的根目录下应该有一个更通用的 try catch 块来捕获任何无法处理的非致命应用程序错误。这通常意味着您作为程序员没有想到这个错误源。稍后您可以扩展代码以处理此错误。但是执行根不应该是你想到异常处理的唯一地方。
您的示例
在关于 ArgumentNullException 的示例中:
请参阅以下有关异常处理的一般想法:
处理方法中的例外
如果在DoSomethingWith方法中抛出异常并且您可以处理它并继续执行流程而没有任何问题,那么您当然应该这样做。
这是重试数据库操作的伪代码示例:
void DoSomethingAndRetry(value)
{
try
{
SaveToDatabase(value);
}
catch(DeadlockException ex)
{
//deadlock happened, we are retrying the SQL statement
SaveToDatabase(value);
}
}
让异常冒泡
让我们假设您的方法是公开的。如果发生异常意味着该方法失败并且您无法继续执行,则该异常应该冒泡,以便调用代码可以相应地处理它。它取决于用例如何调用代码对异常做出反应。
在让异常冒泡之前,您可以将其作为内部异常包装到另一个特定于应用程序的异常中,以添加其他上下文信息。您也可以以某种方式处理异常,例如记录它,或者将日志记录保留到调用代码,具体取决于您的日志记录体系结构。
public bool SaveSomething(value)
{
try
{
SaveToFile(value);
}
catch(FileNotFoundException ex)
{
//process exception if needed, E.g. log it
ProcessException(ex);
//you may want to wrap this exception into another one to add context info
throw WrapIntoNewExceptionWithSomeDetails(ex);
}
}
记录可能的例外情况
在.NET中,定义方法抛出的异常以及抛出它的原因也很有帮助。因此调用代码可以考虑这一点。见https://msdn.microsoft.com/en-us/library/w1htk11d.aspx
示例:
/// <exception cref="System.Exception">Thrown when something happens..</exception>
DoSomethingWith(value)
{
...
}
忽略例外
对于使用失败方法并且不想一直添加try catch块的方法,您可以创建一个具有类似签名的方法:
public bool TryDoSomethingWith(value)
{
try
{
DoSomethingWith(value);
return true;
}
catch(Exception ex)
{
//process exception if needed, e.g. log it
ProcessException(ex);
return false;
}
}