views:

209

answers:

6

The following is pseudocode:

myGoto:
try
{
   // do some db updating
   myDB.doOptimisticConcurrency();

} catch (MyConcExeption ex) {

   if (tried < fiveTimes) {
       myDB.Refresh();
       tried++;
       goto myGoto;
   }

}

I have several try-catch blocks in one method, and I don't want to reinvoke my method from the beginning for every thrown exception. Is using goto acceptable in this situation?

+2  A: 

Using goto is almost never acceptable, it leads to spaghetti code and get your code less readable.

In your case a simple loop would make your code more readable.

alt text

Colin Hebert
Wrong. GOTO's can improve your code if used wisely. For example, in C it can be useful for wrapping error handling code in on e place if many calls in a function may fail. That said, I have never found the need to use it in C#, and I don't like the OP's example. Absolutes are rarely correct though.
Ed Swangren
@Ed Swangren, I tempered my statement ;)
Colin Hebert
Ok, now I agree. Removed my downvote :)
Ed Swangren
+1 for the answer and referencing xkcd :-)
WestDiscGolf
I hope you don't miss that the point of the xkcd comic is that it is _not_ the end of the world to use goto ...
Freed
+3  A: 

You could just use a loop.

Tyler
+14  A: 

You can change it to:

while (tried < fiveTimes)
try
{
   // do some db updating
   myDB.doOptimisticConcurrency();
   break;
}
catch (MyConcExeption ex)
{
   tried++;
   myDB.Refresh();
}
Giorgi
Note that at the end of this loop, you would have to then test `tried` again to know whether or not it succeeded. I'd generally prefer an exception after the retries had all failed.
Jon Skeet
@Jon - That's true for the code in the question too, I just showed how to write it without goto.
Giorgi
@Giorgi: Granted, but I think it's worth pointing it out as a flaw in the original code to. I suspect it's very rare that you actually want to continue as if nothing's gone wrong after several retries.
Jon Skeet
+1  A: 

You shouldn't be throwing an exception if the situation is not, well, exceptional. Why don't you add a retry argument to that method and throw the exception internally only if the retry count is exceeded?

EDIT: Like others have suggested, a loop is a better alternative as well. However, that looks like your method, not a method wrapped in some library that you can't modify. If I am correct, I would still go with the retry parameter and only throw an exception if all retries fail. If you expect that this method will sometimes fail on the first attempt that should not be an exception.

Ed Swangren
+11  A: 

I wouldn't use "goto" - but you might want to write a little helper method. For example:

public static void TryMultiple<E>(Action action, int times) where E : Exception
{
    E lastException = null;
    for (int i = 0; i < times; i++)
    {
        try
        {
            action();
            return; // Yay, success!
        }
        catch (E exception)
        {
            // Possibly log?
            lastException = exception;
        }
    }
    throw new RetryFailedException("Retry failed " + times + " times",
                                   lastException);
}

That's just a sketch of the solution - you'll want to adapt it accordingly. Anyway, this basically lets you perform retries in the face of a semi-accepted exception in a reusable manner. You'll probably use a lambda expression to express the action, or sometimes just a method group for a single method call:

TryMultiple<MyConcurrencyException>(myDB.doOptimisticConcurrency, 5);
Jon Skeet
A: 

This is much better:

private void MyMethod()
{
   MyMethod(5);
}    

private void MyMethod(int triesLeft)
{
   if(triesLeft == 0)
      return;  // or throw

   try
   {
      // do some db updating
      myDB.doOptimisticConcurrency();
   }       
   catch (MyConcExeption ex) 
   {
       myDB.Refresh(); 
       MyMethod(triesLeft - 1);
   }
}
Ani
It's a loop! Why bring recursion into it?
Freed
Loops and recursion are equivalent.
Ani
yeah... let's not write Lisp in C# if we can avoid it by using a simple loop please.
Ed Swangren
@Ani - all turing complete languages are "equivalent", but loops are primary constructs that follow the style of imperative languages much closer than recursion, not to mention the potential stack overflows ...
Freed
@Ed Swangren: I contest your claim that loops are always 'simple' in comparison with recursion. In this case, I find that a try-catch nested inside a loop is slightly harder to read.
Ani
@Ed Swangren: Don't you think it's a little dogmatic to equate recursion with Lisp and loops with C#? It's about the task at hand, and in this case, I find both methods to be acceptable.
Ani
@Ani - loops are definitely not always easier to read than recursion, traversing a binary tree is one example that looks much better with recursion than without in any language. In this case however, all you need to do is to do something n times, which is very well expressed using a loop, and not so well using recursion (in C#).
Freed
@Freed: Sorry, that should have been @Ed Swangren in the previous comment. In any case, I don't understand your point. Firstly, you're not doing something 'n' times, you're doing something until it succeeds or n times have been reached. You could model the terminating condition as a loop condition or as the base case of a recursion. I don't see why the loop is 'fundamentally more pure or 'well expressed' as compared to recursion, in this case.
Ani
I would suspect Ed was refering to this loop as simple, not all loops as simple.
Freed
@Ani - Point being - if you follow the style of a language, people will understand it better.
Freed
@Freed: So is your point really that this trivial recursion is not in the 'style' of C#??
Ani
@Ani - Yes, definitely. I'm not at all contesting that the recursion based solution would work, only that it would be harder for people that work with C# to understand because it does not follow the expected pattern for something that can be seen as a loop.
Freed
@Freed: "Expected pattern for something that can be seen as a loop". In my opinion, your expectations are quite different from many who develop and maintain well-written projects in idiomatic C#.
Ani