20

在某些情况下,我只想调用某个方法来做一些工作,而不关心处理它可能抛出的所有特定异常。相反,我真正关心的是该方法是否成功。

我将提供一个 .NET / C# 示例。假设我有一个要复制的文件,我真正关心的是复制操作是否成功。如果复制失败,我不在乎特定异常是 FileNotFoundException 还是 IOException“磁盘空间不足”异常或其他异常……在这种情况下,我的应用程序将正常运行,因为此操作并不重要。

因此,如何实现这一点的想法是:

try 
{
  // try 
  System.IO.File.Copy(strFile, strFile + ".new");
} 
catch (Exception ex) 
{
  // if critical exception then rethrow
  if (IsCritical(ex)) 
      throw;

  // else just log and swallow...
  Console.WriteLine("Failed to copy the file: " + ex.Message);
}

其中 IsCritical(Exception ex) 是定义为的辅助方法:

public static bool IsCritical(Exception ex) 
{
  if (ex is OutOfMemoryException) return true;
  if (ex is AppDomainUnloadedException) return true;
  if (ex is BadImageFormatException) return true;
  if (ex is CannotUnloadAppDomainException) return true;
  if (ex is ExecutionEngineException) return true;
  if (ex is InvalidProgramException) return true;
  if (ex is System.Threading.ThreadAbortException) 
      return true;
  return false;
}

此问题基于以下文章:C# 中的异常处理,并牢记“不要捕获您无法处理的异常”规则

这个想法是遵循异常处理最佳实践的主要规则: - 不要在不重新抛出的情况下捕获一般异常 - 只捕获你知道如何处理的异常 - (在这种情况下,我想以相同的方式处理它们......通过记录并继续应用程序逻辑)。

那么对于给定的场景,这是一个好方法吗?如果不是,为什么以及做什么会更好?

4

7 回答 7

22

通常建议不要吞下异常的原因是它可以隐藏错误。例如,您正在做的不是做 a File.Copy: 您也在做字符串处理 ( strFile + ".new")。这不能抛出(OOM除外),但如果计算更复杂,你可能隐藏了一个错误

在这种情况下,您可能应该将所有计算移出 try 块。然后可以吞下任何异常。我有记录它们的习惯,以防尽管我很小心,但我仍然犯了错误。

避免不必要地吞咽的规则是为了保护开发人员免于犯错误。如果你有理由确定一切都很好,那么你就不需要遵守规则。

于 2013-11-11T10:15:02.053 回答
9

这条线让我有点担心...

如果复制失败,我不在乎特定异常是 FileNotFoundException 还是 IOException “磁盘空间不足”异常或其他异常

与其说是 FNF 异常,不如说是“磁盘空间不足”等 - 这些是您可能不想忽略的异常。原因是,如果没有足够的磁盘空间,理论上,你的应用程序最终会失败。这实际上是您不应该捕获一般异常的主要原因之一,因为您有效地掩盖了此类更大的问题。

在更一般的说明中,为了更具体地回答您的问题,捕获一个更一般的异常是完全可以的,您确信它不会对您的应用程序产生任何重大影响,也不会如前所述(我重新迭代充分的理由),不会掩盖任何更大/更严重的问题。

于 2013-11-11T10:16:27.053 回答
6

在特定情况下吞下特定异常是可以的,但实际上它取决于用例。

我建议处理异常,您可以处理和使用AppDomain.UnhandledException事件来处理未处理的异常,并告知用户发生了什么。

从调试的角度来看,这并不重要,只要您可以访问代码,因为您可以在 Visual Studio 中启用中断所有常见的运行时异常。(调试 -> 异常 -> 公共语言运行时异常 -> 选中左侧复选框)

我永远不会依赖关键异常列表,因为您并不真正知道列表是否完整。

于 2013-11-11T11:01:30.607 回答
0

我倾向于说,任何来自您不是自己编写的代码的异常都应该被视为严重异常,因为这意味着引发错误的系统很可能处于未定义状态,因此您的任何后续操作无法保证对该系统的尝试成功。

因此我可能会做类似的事情:

  try 
    {
      System.IO.File.Copy(strFile, strFile + ".new");
     //MyLibrary throws exception clases I defined so I know what they are all about
      MyLibrary.SomeClass.DoSomething(strFile);
    }
    catch(ExceptionTypeIDefinedInMyLibraryWhichIKnowICanSafelyIgnore iex)
    {
      Console.WriteLine("Not to worry: "+ iex.Message);
    }
    catch (Exception ex) 
    {
      //This absolute is not something I can handle. Log it and throw.
      Log(ex); throw;
    }

这样,您就不能忽略关键异常,并且您只能从您自己的子系统中抛出特定类型的异常,因为您可以控制它们抛出的内容。

但是,如果您真的想遵循您演示的模式,我倾向于扭转这种情况,只处理您知道可以处理的异常,并抛出其他所有内容。

这样,当新的OhMyGodThereAreZombiesInTheServer异常被添加到 BCL 时,你就不会被烧毁,你永远不知道,它可能会发生......

IE

try 
{
  // try 
  System.IO.File.Copy(strFile, strFile + ".new");
} 
catch (Exception ex) 
{
  // if critical exception then rethrow
  if (ICanHandleThis(ex)) 
  {
   Console.WriteLine("Failed to copy the file: " + ex.Message);
  }
  else
  {
   //step aside and let Dr McNinja deal with it
   throw;
  }     
}

public static bool ICanHandleThis(Exception ex) 
{
 //I don't care about these exceptions, they aren't critical
 //to my application for some reason.
 return (ex is SomeTrivialExceptionTypeIDontCareAbout 
        || ex is SomeOtherIgnorableExceptionType);

}
于 2013-11-12T11:55:02.767 回答
0

您可以为同一次尝试添加多个 catch 语句:

try
{
 //code here
}
 catch (FileNotFoundException ex1) 
{
  //Do whatever you want
}
catch (IOException ex2)
{
  //Do whatever you want
}

我建议处理您认为不重要的异常,其余的只做一个catch (Exception er){throw ;}

也只是对于这两个特定的异常就足以捕获一个IOException,因为IOExceptionFileNotFountException的父类

于 2013-11-11T10:18:30.460 回答
0

我想你回答了你自己的问题。这完全取决于您的业务逻辑。但一般来说,如果我会“吞下”异常,但只能通过它们的特定类型,同样,你只是以另一种方式。

于 2013-11-11T10:29:43.047 回答
0

如果您希望您的程序不可调试并且管理员讨厌您,则可以吞下异常。

您至少需要在较低的日志级别debugtracefine其他任何地方记录异常。

它将有助于调试问题、了解程序如何工作或为什么没有发生某些事情。

唯一可以吞下的例外是那些绝对不会对应用程序产生影响的非常罕见的例外(例如睡眠中断例外)。看到这篇好文章

现在在您的示例中,您并没有完全吞下异常,而是在丢弃堆栈跟踪时打印消息。

只需确保异常消息包含有关复制的源和目标的信息,以便用户可以对其进行调试。同样取决于您的应用程序,了解应用程序代码的哪一部分正在执行复制可能会很有用。有时可能需要它。

于 2020-03-17T11:36:49.923 回答