C#异常反模式

示例

吞咽异常

应该始终以以下方式重新引发异常:

try
{
    ...
}
catch (Exception ex)
{
    ...
    throw;
}

重新抛出如下所示的异常将混淆原始异常,并丢失原始堆栈跟踪。一个人绝对不要这样做!捕获和重新抛出之前的堆栈跟踪将丢失。

try
{
    ...
}
catch (Exception ex)
{
    ...
    throw ex;
}

棒球异常处理

人们不应该使用异常代替常规的流控制结构,例如if-then语句和while循环。这种反模式有时称为“棒球异常处理”。

这是反模式的示例:

try
{
    while (AccountManager.HasMoreAccounts())
    {
        account = AccountManager.GetNextAccount();
        if (account.Name == userName)
        {
            //我们找到了
            throw new AccountFoundException(account);
        }
    }
}
catch (AccountFoundException found)
{
    Console.Write("这是您的帐户详细信息: " + found.Account.Details.ToString());
}

这是一种更好的方法:

Account found = null;
while (AccountManager.HasMoreAccounts() && (found==null))
{
    account = AccountManager.GetNextAccount();
    if (account.Name == userName)
    {
        //我们找到了
        found = account;
    }
}
Console.Write("这是您的帐户详细信息: " + found.Details.ToString());

捕获(异常)

在代码中几乎没有(有人说不!)捕获通用异常类型的原因。您应该只捕获预期会发生的异常类型,因为否则会在代码中隐藏错误。

try 
{
     var f = File.Open(myfile);
     // 做点什么
}
catch (Exception x)
{
     // 假设找不到文件
     Console.Write("Could not open file");
     // 但可能由于文件处理代码中的错误而导致错误是NullReferenceException?
}

最好做:

try 
{
     var f = File.Open(myfile);
     // 做点什么 which should normally not throw exceptions
}
catch (IOException)
{
     Console.Write("File not found");
}
// 不幸的是,这个不是从上面得出的,所以单独声明
catch (UnauthorizedAccessException) 
{
     Console.Write("Insufficient rights");
}

如果发生任何其他异常,我们有意让应用程序崩溃,因此它直接进入调试器,我们可以解决问题。我们绝对不能在程序之外发生任何其他异常的情况下交付程序,因此发生崩溃不是问题。

以下示例也是一个不好的例子,因为它使用异常来解决编程错误。那不是他们设计的目的。

public void DoSomething(String s)
{
     if (s == null)
         throw new ArgumentNullException(nameof(s));
     // 实施在这里
}

try 
{    
     DoSomething(myString);
}
catch(ArgumentNullException x)
{
    // 如果发生这种情况,我们会出现编程错误,我们应该检查
    // 为什么myString首先是null。
}