【问题标题】:Try/Catch exception continue from line that caused the exceptionTry/Catch 异常从导致异常的行继续
【发布时间】:2011-08-19 17:42:23
【问题描述】:

当抛出异常时,如何捕获它然后从导致错误的行开始继续执行?

编辑: 我们的程序与 Indesign 服务器通信,该服务器一直崩溃并抛出随机的 COM 相关错误(这些错误与服务器本身的错误有关)。 Indesign Server 也需要很长时间来处理命令,因此当它崩溃时,我们希望避免重新启动执行。相反,我们希望从导致异常的行继续。程序中的任何一行都可能导致异常。所以从技术上讲,我们不能使用循环。

【问题讨论】:

  • 你的意思是 next 行吗?还是实际引发异常的那一行?
  • @dlev:我认为他指的是引发异常的行之后的行。他可能只有一次尝试/捕获,而不是不止一次。
  • 你为什么要这样做?
  • 请不要复活名为“On Error Resume Next”的古老邪恶。
  • @丹·布莱恩特:+1!这是我读到这个问题时首先想到的。不幸的是,许多答案虽然是正确的,但正在复活它。

标签: c#


【解决方案1】:

当抛出异常时,如何捕获它然后从导致错误的行开始继续执行? (不是下一行;重试导致异常的行。)

不要尝试这样做。你从错误的方向来解决这个问题。

问题是您的子系统不可靠。您有一个处理该不可靠子系统的理想策略,即重试操作直到成功。如果是这种情况,则不要将该逻辑放在使用子系统的业务线代码中。业务线代码应该与业务逻辑有关,而不是与您选择处理易碎子系统的机制有关。 将机制隔离到特定的类,使不可靠的子系统成为可靠的子系统。

即构建一个与不可靠子系统具有相同接口的代理类,并将您的重试逻辑隔离到该代理类中。然后业务线代码可以使用代理类作为可靠的子系统。

也就是说,“重试直到成功”的策略可能是一个糟糕的策略。如果子系统真的被破坏了,而不仅仅是暂时的不稳定,那么“重试直到它工作”意味着“永远等待”,大多数用户不喜欢永远等待。例如,如果异常是由于路由器被拔出而不是某种暂时情况造成的,那么在有人重新插入路由器之前一直呆在那里循环似乎是个坏主意。

【讨论】:

  • 重试失败也不是一种与自身组合良好的模式,应谨慎使用。由于级联重试逻辑,创建严重延迟报告实际错误情况的系统非常容易。
  • @LBushkin:我完全同意。请参阅blogs.msdn.com/b/oldnewthing/archive/2005/11/07/489807.aspx 了解一些轶事。
  • 大多数用户不喜欢永远等待 - 你能证明这一点吗?让一些用户永远等待,然后问他们是否喜欢它。您可能会对您的发现感到惊讶!
【解决方案2】:

如果您正在寻找通用的东西,那么使用 lambda 就可以了。例如

public static class Exception {
  public static void Continue(Action action) {
    try {
      action();  
    } catch { 
      // Log 
    }
  }
}

Exception.Continue(() => Statement1());
Exception.Continue(() => Statement2());

我不认为这是大规模使用的理想解决方案。它会为您使用它的每个语句导致额外的委托分配、委托调用和方法调用。相反,我将专注于识别导致您出现问题的函数并为它们单独添加显式包装器。

【讨论】:

  • 有趣的方法,但如果使用Pretend.EverythingIsFine(() => SomethingDangerous());调用它可能会更具描述性
  • @Dan 我更喜欢Ignorance.IsBliss(() => ...) :)
【解决方案3】:

您必须将任何可能引发异常的行括在其自己的 try/catch 块中才能完成。

所以不是

try
{
    StatementOne();  // Exception thrown here
    StatementTwo();
}
catch (SOneException) { ... }

你必须这样做:

try
{
    StatementOne();
}
catch (SOneException) { ... }

StatementTwo();

如果由于(希望瞬态)异常而需要重试操作,您可以使用这样的方法:

public static class ExceptionHelper
{
    public static void TryNTimesAndThenThrow(Action statement, int retryCount)
    {
        bool keepTrying = false;
        do
        {
            try
            {
                statement();
                keepTrying = false;
            }
            catch (Exception)
            {
                if (retryCount > 0)
                {
                    keepTrying = true;
                    retryCount--;
                }
                else
                {
                    // If it doesn't work here, assume it's broken and rethrow
                    throw;
                }
            }
        } while (keepTrying)
    }
}

那么你可以写:

ExceptionHelper.TryNTimesAndThenThrow(() => MightThrowATransientException(), 3);

请记住,这两种方法都应谨慎使用。前者会使您的代码混乱很多,而后者最终可能会花费比您想象的更多的时间(因为如果发生意外情况,简单地提醒用户通常是一个更好的主意。因此,您需要强调临时异常如果你再试一次,真的期望会消失。)

【讨论】:

    【解决方案4】:

    你可以这样做:

                    //Retry logic on opening the connection
                    int retries = 0;
                    openconnection:
                        try
                        {
                            connection.Open();
                        }
                        catch
                        {
                            retries++;
                            //Wait 2 seconds
                            System.Threading.Thread.Sleep(2000);
                            if (retries < MAXRETRIES)
                            {
                                goto openconnection;
                            }
                            else
                            {
                                throw;
                            }
                        }
    

    【讨论】:

    • 这将需要大量的标签和 goto 语句,因为程序的任何一行都可能引发异常。也许有一种方法可以使用 .NET 反射。
    • 如果有我们想要忽略或重试的间歇性异常,这很有效。在大多数情况下,我不会使用这种方法,但在这里我可以执行同一行,直到成功或最终失败。另外,我已经隔离了我想重试的线路。如果您有很多行可能会抛出异常并且需要更多标签并且会很混乱,那么您是正确的。
    猜你喜欢
    • 1970-01-01
    • 2011-09-16
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 2013-03-09
    • 2011-04-01
    • 1970-01-01
    相关资源
    最近更新 更多