【问题标题】:A lot of catch blocks, but in all of them the same function很多 catch 块,但它们都具有相同的功能
【发布时间】:2012-12-27 05:13:12
【问题描述】:

如果我有这样的事情怎么办:

try
{
   //work
}
catch (ArgumentNullException e)
{
   HandleNullException();
   Logger.log("ArgumentNullException " + e);
   DoSomething();
}
catch (SomeOtherException e)
{
   HandleSomeOtherException();
   Logger.log("SomeOtherException " + e);
   DoSomething();
}
catch (Exception e)
{
   HandleException();
   Logger.log("Exception " + e);
   DoSomething();
}

现在我们可以看到,我正在尝试处理一些不同情况的异常。但是每当引发异常时,我总是在最后调用方法DoSomething()。如果出现异常,是否有更聪明的方法来调用DoSomething()?如果我添加了一个finally 块并在那里调用DoSomething(),它总是会被调用,即使没有例外。有什么建议吗?

【问题讨论】:

  • @JanesAbouChleih 然后,他没有冗余代码来调用方法,而是有冗余代码来设置布尔值。
  • @Eve:用冗余的标志设置替换冗余的方法调用仍然是一种改进。让我们假设 OP 在某个时候想要用DoSomethingElse 替换DoSomething。有了标志,他/她只需在一处修改代码。如果没有标志,可能需要以相同的方式修改多个位置。如果 OP 忽略了其中一个怎么办?所以恕我直言,引入标志可以改善 DRY(不要重复自己)并消除潜在的错误源。
  • @stakx 使用我的解决方案(只有一个 catch 块,请参阅我的答案),我认为他消除了“DRY”(代码重复),而没有引入像 didAnyCatchBlockRun 这样的痛苦布尔值。
  • @JeppeStigNielsen:请参阅我对您上述评论的回复。

标签: c# .net exception try-catch


【解决方案1】:

您可以在抛出异常时设置布尔值,使用finally 并检查布尔值。 (或者反其道而行之,只在不抛出异常时才设置布尔值,如下图:)

bool noException = false;
try
{
    //work
    noException = true;
}
catch (ArgumentNullException e)
{
    HandleNullException();
    Logger.log("ArgumentNullException " + e);
}
catch (SomeOtherException e)
{
    HandleSomeOtherException();
    Logger.log("SomeOtherException " + e);
}
catch (Exception e)
{
    HandleException();
    Logger.log("Exception " + e);
}
finally
{
    if (!noException)
        DoSomething();
}

【讨论】:

    【解决方案2】:

    为什么不只是:

    try
    {
        //work
    }
    catch (Exception e)
    {
        if (e is ArgumentNullException)
          HandleNullException();
        else if (e is SomeOtherException)
          HandleSomeOtherException();
        else
          HandleException();
        Logger.log(e.GetType().Name + " " + e);
        DoSomething();
    }
    

    记录的类型名称将是实际的运行时类型,因此,例如,如果您在情况 3 中,可能会记录 "IndexOutOfRangeException" 而不是仅记录 "Exception",但我认为这是对您现在的改进。

    编辑: 上面的代码在使用ifelse if 逻辑检查类型时看起来很糟糕。如果我们引入多态,它可能会更漂亮:

    try
    {
        //work
    }
    catch (HandleableException e)
    {
        e.Handle();  // this calls a **virtual** method, each override does what's relevant
        Logger.log(e.GetType().Name + " " + e);
        DoSomething();
    }
    

    当然,如果我们无法修改某些Exception 类,那么就不可能给它们一个Handle() 方法。 .Handle() 也可以是扩展方法(而不是 virtual 实例方法),但是类型检查(丑陋的代码)必须在该方法内完成。然后这变得与 Eve 的答案非常相似。

    【讨论】:

    • -1。这解决了问题,但代价是在代码中引入了比 OP 试图摆脱的更糟糕的问题! if (… is FooException) … else if (… is BarException) … 违背了两个基本原则:(1) 正是您可以拥有多个 catch 处理程序的原因;和(2) OOP 多态性。关于最后一点,请参见例如2008 年的谈话,'Conditionals and Polymorphism' by Misko Hevery.
    • @stakx 代码一开始就不太漂亮。你喜欢几个catch 块吗(其中一些是相关的,比如ExceptionSomeOtherException 的基类,而SomeOtherException 不是 ArgumentNullException 的基类) ?在我看来 if (e is T) … else if (e is S) 等等,丑陋的,但是几个 catch 块是同样丑陋的。而且我不喜欢“分解”许多catch 块共有的逻辑并将其放入带有模糊iffinally 块的解决方案。 finally 块是为在任何情况下都必须运行的东西而设计的。
    • +1 为您的最新添加。但是请注意,一堆扩展方法 Handle(this SomeExceptionType) 不会像您预期的那样工作:例如,如果您同时拥有 Handle(this Exception)Handle(this FooException),那么即使是 FooException 实例,也只会调用第一个方法。这是因为e.Handle() 中的e 具有静态(编译时)类型Exception。要调用的方法由编译器静态解析,基于静态类型信息。也就是说,没有带有扩展方法的虚拟调度。
    • @stakx 你说的完全正确。我又进行了一次编辑以删除误导性部分。根据实际运行时类型获得重载解决方案的唯一方法是将绑定推迟到运行时(可以使用 dynamic 关键字 Handle((dynamic)e); 完成)。
    【解决方案3】:

    如果我添加一个finally 块并在那里调用DoSomething(),它总是会被调用,即使没有异常。

    您要查找的内容在 CLI standard(分区 IIA,第 18 章)中称为 fault handler。尽管 .NET 实现了它们,但 C# 语言并不直接支持它们。但是,它们可以被模拟:

    bool success = false;
    try
    {
        …
        success = true;
    }
    catch (…)
    {
        …
    }
    …
    finally
    {
        if (!success)
        {
            DoSomething();
        }
    }
    

    请注意,不需要在每个 catch 处理程序中设置标志,正如这里的一些答案所建议的那样。简单地否定测试,你只需要设置一次标志,在try块的末尾。

    【讨论】:

      【解决方案4】:

      您可以使用以下代码,实际上确实删除了冗余。

      try
      {
          //work
      }
      catch (Exception e)
      {
          Handle(e);
      }
      

      Handle 方法在哪里:

      static void Handle(Exception e)
      {
          var exceptionType = e.GetType();
          //Use an if/else block, or use a Dictionary<Type, Action>
          //to operate on your exception
          Logger.log(exceptionType + " " + e);
          DoSomething();
      }
      

      【讨论】:

      • IMO this 和 @stakx 方法是两者的最佳 +1。
      【解决方案5】:

      你可以这样做:

      Exception caughtException = null;
      try {
          ...
      } catch(ExceptionType1 e1) {
          ...
          caughtException = e1;
      } catch(ExceptionType2 e2) {
          ...
          caughtException = e2;
      } catch(ExceptionType3 e3) {
          ...
          caughtException = e3;
      }
      if (caughtException != null) {
          // Put your common exception code here
          DoSomenthing();
          // You can pass caughtException to functions, too
          LogException(caughtException);
      }
      

      caughtException 将是 null 除非捕获到异常。您可以使用它来决定是否拨打DoSomenthing

      【讨论】:

        【解决方案6】:

        你现在所做的已经差不多了。

        如果您需要在异常发生时调用此函数,但不是其他情况,并且您必须有不同的代码来处理不同的异常,那么这是可以做到的最好的。

        【讨论】:

        • 如果要做的大部分工作不取决于正在处理的接收类型,他可以只拥有一个catch 块,并通过说if (e is ArgumentNullException) 等来处理微小的差异在。如果不清楚我的意思,请参阅我的回答。
        猜你喜欢
        • 1970-01-01
        • 2011-01-07
        • 2020-03-22
        • 1970-01-01
        • 2018-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-11
        相关资源
        最近更新 更多