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

不管怎样,问题是这样的:

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

问题在于,此类的多个方法中有一堆 IO 操作,并且需要将每个操作包装在 try-catch/重试逻辑中。

这是一个示例代码片段:

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

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

有帮助吗?

解决方案

这看起来是了解面向方面编程的绝佳机会。这是一篇关于 .NET 中的 AOP. 。一般的想法是,您将提取跨职能问题(即重试 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 次。在第五次尝试时,原始异常在重试异常内重新引发。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top