【问题标题】:Reducing duplicate error handling code in C#?减少 C# 中重复的错误处理代码?
【发布时间】:2010-09-05 07:37:16
【问题描述】:

我从来没有对异常处理的工作方式完全满意,有很多异常和 try/catch 带来的问题(堆栈展开等),但它似乎打破了很多 OO 模型过程。

不管怎样,问题来了:

假设您有一些类包装或包含网络文件 IO 操作(例如,在某处的某个特定 UNC 路径读取和写入某个文件)。出于各种原因,您不希望这些 IO 操作失败,因此,如果您检测到它们失败,则重试它们并继续重试它们,直到它们成功或达到超时。我已经有一个方便的 RetryTimer 类,我可以实例化并使用它在重试之间休眠当前线程,并确定超时时间何时结束,等等。

问题是你在这个类的几个方法中有一堆IO操作,你需要把它们都包装在try-catch/retry逻辑中。

这是一个示例代码sn-p:

RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10));
bool success = false;
while (!success)
{
    try
    {
        // do some file IO which may succeed or fail
        success = true;
    }
    catch (IOException e)
    {
        if (fileIORetryTimer.HasExceededRetryTimeout)
        {
            throw e;
        }
        fileIORetryTimer.SleepUntilNextRetry();
    }
}

那么,如何避免在整个课程中为每个文件 IO 操作重复大部分代码?我的解决方案是在执行传递给它的委托块的类中使用匿名委托块和单个方法。这让我可以在其他方法中做这样的事情:

this.RetryFileIO( delegate()
    {
        // some code block
    } );

我有点喜欢这个,但它还有很多不足之处。我想听听其他人如何解决这类问题。

【问题讨论】:

  • 仅供参考:almost always better 只是 throw; 而不是 throw e;
  • 我想知道:当被问到这个问题时,C# 3 是最新的。在 C# 8 之前有什么变化(当前版本为 1/16/20)?

标签: c# exception error-handling


【解决方案1】:

这看起来是一个了解面向方面编程的绝佳机会。这是一篇关于AOP in .NET 的好文章。一般的想法是,您将跨功能问题(即重试 x 小时)提取到一个单独的类中,然后注释任何需要以这种方式修改其行为的方法。这是它的外观(在 Int32 上有一个很好的扩展方法)

[RetryFor( 10.Hours() )]
public void DeleteArchive()
{
  //.. code to just delete the archive
}

【讨论】:

    【解决方案2】:

    这是我最近所做的。它可能在其他地方做得更好,但看起来很干净且可重复使用。

    我有一个如下所示的实用方法:

        public delegate void WorkMethod();
    
        static public void DoAndRetry(WorkMethod wm, int maxRetries)
        {
            int curRetries = 0;
            do
            {
                try
                {
                    wm.Invoke();
                    return;
                }
                catch (Exception e)
                {
                    curRetries++;
                    if (curRetries > maxRetries)
                    {
                        throw new Exception("Maximum retries reached", e);
                    }
                }
            } while (true);
        }
    

    然后在我的应用程序中,我使用 c# 的 Lamda 表达式语法来保持整洁:

    Utility.DoAndRetry( () => ie.GoTo(url), 5);
    

    这会调用我的方法并重试最多 5 次。在第五次尝试时,在重试异常中重新抛出原始异常。

    【讨论】:

    • 但是为什么要自定义 WorkMethod 委托而不是 Action
    【解决方案3】:

    您还可以使用更面向对象的方法:

    • 创建一个基类来执行错误处理并调用抽象方法来执行具体工作。 (模板方法模式)
    • 为每个操作创建具体的类。

    这具有命名您执行的每种操作类型的优点,并为您提供命令模式 - 操作已表示为对象。

    【讨论】:

      【解决方案4】:

      只是想知道,您觉得您的方法还有哪些不足之处?您可以将匿名委托替换为..命名的?委托,类似

          public delegate void IoOperation(params string[] parameters);
      
          public void FileDeleteOperation(params string[] fileName)
          {
              File.Delete(fileName[0]);
          }
      
          public void FileCopyOperation(params string[] fileNames)
          {
              File.Copy(fileNames[0], fileNames[1]);
          }
      
          public void RetryFileIO(IoOperation operation, params string[] parameters)
          {
              RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10));
              bool success = false;
              while (!success)
              {
                  try
                  {
                      operation(parameters);
                      success = true;
                  }
                  catch (IOException e)
                  {
                      if (fileIORetryTimer.HasExceededRetryTimeout)
                      {
                          throw;
                      }
                      fileIORetryTimer.SleepUntilNextRetry();
                  }
              }
          }
      
          public void Foo()
          {
              this.RetryFileIO(FileDeleteOperation, "L:\file.to.delete" );
              this.RetryFileIO(FileCopyOperation, "L:\file.to.copy.source", "L:\file.to.copy.destination" );
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多