在C#中减少重复的错误处理代码?

问题描述:

我从来没有完全满意异常处理的工作方式,有很多异常和try/catch带来的表(堆栈展开等),但它似乎打破了很多OO模型的过程。在C#中减少重复的错误处理代码?

不管怎么说,这就是问题所在:

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

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

下面是一个示例代码片段:

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 
    }); 

我喜欢这个有点,但它留下来了很多有待改进。我想听听其他人会如何解决这类问题。

+1

只是一个通用的供参考:它是[几乎*总是*更好](http://philosopherdeveloper.wordpress.com/2010/05/05/re-throwing-caught-exceptions/)只是`抛出;而是`throw e;` – 2010-09-13 03:34:47

这看起来像一个极好的机会来看看面向方面编程。这是一篇关于AOP in .NET的好文章。总的想法是,你需要将交叉功能关注点(即x小时的重试)提取到一个单独的类中,然后注释任何需要以这种方式修改其行为的方法。下面是它看起来可能会(对的Int32一个不错的扩展方法)

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

只是想知道,你觉得你的方法留下了什么?你可以用..命名替换匿名代理?代表,像

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"); 
    } 

你也可以使用一个更面向对象的方法:

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

这样做的好处是可以命名您执行的每种类型的操作,并为您提供命令模式 - 操作已被表示为对象。

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

我有一个看起来像这样的实用方法:

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次。在第五次尝试中,原始异常在重试异常中重新生成。

+0

但为什么自定义`WorkMethod`委托而不是`Action`? – 2010-09-13 03:32:26