代码之家  ›  专栏  ›  技术社区  ›  Wedge

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

  •  35
  • Wedge  · 技术社区  · 18 年前

    我从来没有对异常处理的工作方式完全满意过,有很多异常和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
        } );
    

    我有点喜欢这个,但还是有很多需要。我想听听其他人是如何解决这类问题的。

    4 回复  |  直到 16 年前
        1
  •  13
  •   Mike Minutillo    18 年前

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

    [RetryFor( 10.Hours() )]
    public void DeleteArchive()
    {
      //.. code to just delete the archive
    }
    
        2
  •  4
  •   Chris Marasti-Georg Scott Weinstein    18 年前

    只是想知道,你觉得你的方法还需要什么?您可以用..替换匿名代理。命名的?代表,比如

        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" );
        }
    
        3
  •  2
  •   Andrew Peters    18 年前

    您还可以使用更为OO的方法:

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

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

        4
  •  2
  •   Andrej Kyselica    16 年前

    这是我最近做的。它可能在其他地方做的更好,但它似乎相当干净和可重用。

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

        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次。在第五次尝试时,原始异常在重试异常内部重新引发。