Question

I am aware that doing Flow Control on a program using a try-catch block is bad practice, but I can't see how to do it in another way when the error caught needs a redirection of the code's execution.

For example, let's say I have screen where the user clicks a button, and on the buttons click I execute some code. After the code ends execution, I redirect the user to a new screen....

... But the code execution can failure, and in that case, I need to let the user know of the failure and redirect him to the previous screen.

The way I will do it is:

try
{
     ExcuteCode();
     NavigateToNextScreen();
} 
catch(Exception e) 
{
     Log.Write(e);
     ShowErrorMessage();
     GoBack();
} 

Even if I change this to:

bool hasError = false;

try
{
     ExecuteCode();
} 
catch(Exception e) 
{
      Log.Write(e);
      ShowErrorMessage();
      hasError = true;
 } 

  if(hasError) { GoBack(); } 
  else { NavigateToNextScreen();} 

It's still doing Flow Control inside the try-catch block.

So, is it there a recommend way to handle this properly and avoid this bad practice?

Was it helpful?

Solution

It seems that you have misunderstood what is meant when people say that you shouldn't use exceptions for flow-control in your program.

Throwing an exception and catching that exception elsewhere in your program changes the control flow of the program. In that way, exception handling is a flow-control construct, just like a while loop or a goto statement.

What sets exception handling apart from other flow-control constructs is that exception handling can let you exit multiple function invocations at once, but that power also makes it an expensive operations because the runtime environment has to figure out which catch block to jump to.

The relative cost of handling exceptions is the source for the adagio not to use them for flow-control, but what is actually meant is that you should not use exceptions to realize a flow through your program that is expected to happen with any regularity.
Exceptions are a perfect tool if you encounter an error and need to bail out of multiple levels of you call stack. They are exactly the wrong tool to jump out of a loop because you found what you were looking for.

OTHER TIPS

There are a lot of ways to deal with this. Here's just two ideas:

  • Wrap your try-catch into a method with boolean return type, indicating whether the ExecuteCode() succeeded. The control flow is decided on the usage site then, where you show the error message and go back on failure. It's similar to you second version, but instead of directly modifying a variable that is used for the control flow, you just return the result status and make no assumptions about the caller's control flow.

  • Switch the Exception throwing to a Validation-like pattern. This is like the boolean, but without try-catch. In case of an error, ExecuteCode returns a Failure object with the problem description. This similarly allows you to modify your flow control depending on the result value, without the error detecting code needing to know about it.

In general, most of the ways I can think of are based on having a better type for ExecuteCode. When ExecuteCode() doesn't return anything, but throws the exception, then this is essentially what requires you to move your logic into try and catch. A better return type allows you to simply delegate the control flow decisions based on that.

Licensed under: CC-BY-SA with attribution
scroll top