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

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

无论如何,这是问题所在:

假设您有一些包装或包含网络文件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
    } );

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

0
额外
意见: 2
只是一个总的供参考:它是几乎总是更好来简单地 throw; 而不是 throw e;
额外 作者 Dan Tao,

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

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

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

你也可以使用更多的OO方法:

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

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

0
额外

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

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

    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
额外 作者 Dan Tao,