3

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

是否存在模式或其他东西来做到这一点?或者您会建议对该代码进行哪些改进?

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

3 回答 3

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);
}
于 2012-12-20T15:53:25.760 回答
1

我编写了以下代码来做基本相同的事情。它还允许您指定要捕获的异常类型以及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这将阻止重试的发生。

于 2012-12-20T16:02:03.723 回答
1

我看不出代码有什么问题,我只是质疑你的假设。

我看到的几个问题:

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

根据您的需要,您的代码可能就足够了,但要以更健壮的方式封装不可靠的资源,请查看断路器模式。

于 2012-12-20T16:24:59.447 回答