抛出异常时执行某些操作的最佳方式(模式)

时间:2012-12-20 15:47:50

标签: c# exception-handling for-loop try-catch

下面的代码显示了在捕获异常时尝试封装逻辑以重新运行某些内容。

是否存在模式或其他内容?或者您对该代码有什么改进建议?

    public static void DoWhileFailing(int triesAmount, int pauseAmongTries, Action codeToTryRun) {
        bool passed = false;
        Exception lastException = null;

        for (int i = 0; !passed && i < triesAmount; i++) {
            try {
                if (i > 0) {
                    Thread.Sleep(pauseAmongTries);
                }
                codeToTryRun();
                passed = true;
            } catch(Exception e) {
                lastException = e;
            }
        }

        if (!passed && lastException != null) {
            throw new Exception(String.Format("Something failed more than {0} times. That is the last exception catched.", triesAmount), lastException);
        }
    }

3 个答案:

答案 0 :(得分:3)

我会重写这个以消除一些变量,但一般来说你的代码没问题:

public static void DoWhileFailing(int triesAmount, int pauseAmongTries, Action codeToTryRun) {
    if (triesAmount<= 0) {
        throw new ArgumentException("triesAmount");
    }
    Exception ex = null;
    for (int i = 0; i < triesAmount; i++) {
        try {
            codeToTryRun();
            return;
        } catch(Exception e) {
            ex = e;
        }
        Thread.Sleep(pauseAmongTries);
    }
    throw new Exception(String.Format("Something failed more than {0} times. That is the last exception catched.", triesAmount, ex);
}

答案 1 :(得分:1)

我写了下面的代码来做基本相同的事情。它还允许您指定要捕获的Exception的类型,以及确定当前迭代是否应抛出异常或继续重试的Func

public static void RetryBeforeThrow<T>(
    this Action action, 
    Func<T, int, bool> shouldThrow, 
    int waitTime) where T : Exception
{
    if (action == null)
        throw new ArgumentNullException("action");
    if (shouldThrow == null)
        throw new ArgumentNullException("shouldThrow");
    if (waitTime <= 0)
        throw new ArgumentException("Should be greater than zero.", "waitTime");
    int tries = 0;
    do
    {
        try
        {
            action();
            return;
        }
        catch (T ex)
        {
            if (shouldThrow(ex, ++tries))
                throw;
            Thread.Sleep(waitTime);
        }
    }
    while (true);
}

然后你就可以这样称呼它

Action a = () => 
    { 
        //do stuff 
    };
a.RetryBeforeThrow<Exception>((e, i) => i >= 5, 1000);

您可以指定任何异常类型,并且可以检查Func中的异常以确定它是否是您要抛出或继续重试的异常。这使您能够在Action中抛出自己的异常,以阻止重试的发生。

答案 2 :(得分:1)

我没有看到代码有任何问题,我只会质疑你的假设。

我看到了几个问题:

  • 客户需要了解被调用操作的失败模式,以选择正确的参数。
  • 行动可能会间歇性地失败,这是一种容量杀手。像这样的代码不能很好地扩展。
  • 客户可以等待一段不确定的时间来完成操作。
  • 所有例外情况都会被吞下,但可能会隐藏重要的诊断信息。

根据您的需要,您的代码可能就足够了,但是为了更好地封装不可靠的资源,请查看circuit breaker模式。