简体   繁体   中英

Try/Catch exception continue from line that caused the exception

When an exception is thrown, how can I catch it and then continue execution starting from the line that caused the error?

EDIT: Our program communicates with Indesign Server which crashes all the time and throws random COM related errors (these errors have to do with bugs in the Server itself). Indesign Server also takes a very long time to process commands so when it crashes, we want to avoid restarting execution. Instead, we want to continue from the line that caused the exception. Any line in the program can cause an exception. So technically, we cannot use a loop.

When an exception is thrown, how can I catch it and then continue execution starting from the line that caused the error? (Not the next line; retry the line that caused the exception.)

Do not try to do that. You're approaching this problem from the wrong direction.

The problem is that you have an unreliable subsystem. You have a desired policy for dealing with that unreliable subsystem, which is to retry the operation until it succeeds. If that's the case then don't put that logic in the line-of-business code which uses the subsystem . The line-of-business code should be about the business logic, not about the mechanism you choose to deal with the flaky subsystem. Isolate the mechanism to a specific class which makes the unreliable subsystem into a reliable subsystem.

That is, build a proxy class that has the same interface as the unreliable subsystem, and isolate your retry logic into that proxy class. Then the line-of-business code can use the proxy class as a reliable subsystem.

That said, a policy of "retry it until it works" is possibly a bad policy. If the subsystem is genuinely broken and not just flaky in some transient way, then "retry until it works" means "wait forever", and most users do not like waiting forever. For example, if the exception is a result of a router being unplugged rather than some transient condition then sitting there in a loop until someone plugs the router back in seems like a bad idea.

You would have to surround any line that could throw an exception in its own try / catch block to accomplish that.

So instead of

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

You would have to do:

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

StatementTwo();

If you need to retry an operation due to a (hopefully transient ) exception, you can have a method like this:

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

Then you can just write:

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

Keep in mind both methods should be used sparingly. The former will clutter your code quite a bit, while the latter could end up taking a lot more time than you think (since its often a better idea to simply alert the user if something unexpected occurs. Thus the emphasis on a transient exception that you really do expect will disappear if you just try again.)

If you're looking for something general purpose then using a lambda would do the trick. For example

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

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

I wouldn't consider this an ideal solution though for large scale use. It causes an extra delegate allocation, delegate invocation and method invocation for every statement you use this on. Instead I would focus on identifying the functions which are causing you problems and add explicit wrappers for them individually.

You could do something like this:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM