6

I have a method Foo() does some hard work and on the UI layer I have a button to invoke that method.
All I want to do is call the method and show a messagebox if there is something wrong with the Foo() method.

I have two choices to design the method signature:

1.Tuple

Tuple<bool, string> Foo()  
{  
    if(!DoHardWorkA()) return Tuple.New(false, "cannot do hardwork A");  
    if(!DoHardWorkB()) return Tuple.New(false, "cannot do hardwork B");  

    return Tuple.New(true, String.Empty);
}

2.Exception

void Foo()
{
    if(!DoHardWorkA()) throw new ProgramSpecificException("cannot do hardwork A");
    if(!DoHardWorkB()) throw new ProgramSpecificException("cannot do hardwork B");

    return Tuple.New(true, String.Empty);
}

Both DoHardWorkA() and DoHardWorkB() are external methods that I don't have control of them, and they return true/false indicating the result.

Logically, I think I should go with option 2 as they are really exceptions; But for consistency, I'd like to go with option 1.

Which one do you prefer, why?

Amitabh
  • 59,111
  • 42
  • 110
  • 159
AZ.
  • 7,333
  • 6
  • 44
  • 62
  • Is this c# ? You should tag your question with the language your using. – asawyer Mar 21 '12 at 18:10
  • 6
    The important question is: *is failure exceptionally rare, or common?* If it is exceptionally rare then *throw an exception and then handle it*. If failure is commonplace then *return data that reports on the nature of the failure* and process that data like any other data. – Eric Lippert Mar 21 '12 at 18:49
  • Returning the operation result is much better in design, in my opinion. The use of throwing exception should be minimised to the uncontrollable circustances like reading a file, connecting to a database, or making an api request – Andrew Chaa Dec 12 '20 at 21:58
  • Does this answer your question? [Choosing between exception and return value](https://stackoverflow.com/questions/5460101/choosing-between-exception-and-return-value) – Michael Freidgeim Nov 07 '21 at 11:00

5 Answers5

2

Throwing an Exception and handling it in consistent way is better.

If Foo fails for any other reason then also it will be handled. Suppose a scenerio.

void UIMethod()
{
   Tuple<Result, Error> ret = Foo();
   if(ret.Error)
     MessageBox.Show(ret.Error);
}

Now because of change in requirement you have to call another method before Foo and it can also throw an exception. Then it becomes complex.

It is much easier to do this.

void UIMethod()
{
   try{  
       MethodBeforeFoo();
       var ret = Foo();
    }
   catch(Exception ex)
    {
       MessageBox.Show(ex.Message); 
    }

}
Amitabh
  • 59,111
  • 42
  • 110
  • 159
  • the use of exceptions for control flow is an anti-pattern. https://softwareengineering.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why – Michael Freidgeim Nov 07 '21 at 07:40
1

It really depends on your need. Modify your code like this, it will handle unhandled exceptions as well.

   Tuple<bool, string> Foo()  
{  
try
{
    if(!DoHardWorkA()) return Tuple.New(false, "cannot do hardwork A");  
    if(!DoHardWorkB()) return Tuple.New(false, "cannot do hardwork B");  

    return Tuple.New(true, String.Empty);
}
catch
{
  return Tuple.New(false, "cannot do hardwork A"); 
}

}

Shailesh
  • 1,178
  • 11
  • 12
1

If all you are doing is calling these external methods to do some work, and this method you are writing is a wrapper around them, then why throw an exception, "handle" the issue in your method and proceed, throwing and handling exceptions is many many times more expensive

In your particular case, all you are doing is doing some work, and displaying a messagebox to show whether it executed properly, so I'd go with option 1

Note that if you are just trapping the exception and not unwinding the stack the cost is fairly minimal. It's only expensive when you unwind stack, like ex.ToString() or ex.StackTrace

Jason
  • 3,844
  • 1
  • 21
  • 40
  • Exceptions are not expensive https://stackoverflow.com/a/891230/350188, but as a developer you are probably used to seeing them thrown in a debug environment, in which case it is probably the debugger that is slowing down execution. – Stephen Turner Apr 04 '18 at 12:19
  • @StephenTurner Pre-fill a large List (10k or 100k) with a mix of valid and invalid string representation of int, then loop through and attempt to .Parse every element. If you trap the ex and unwind the stack, you should see the app run much slower, even without debugger attached. Surely, if you don't do anything with the exception and simply swallow it, there is little to no perf hit – Jason Apr 04 '18 at 20:42
  • I did: stackoverflow.com/a/49693875/350188 0.042ms without the debugger, 15ms with. – Stephen Turner Apr 06 '18 at 13:58
  • @StephenTurner you are not unwinding the stack, that is the part that makes it expensive. Try changing this _catch (Exception ex) { exceptions.Add(ex); }_ to this **catch (Exception ex) { ex.ToString(); }** and run without debugger. Perhaps I should have clarified in my response...though for what it's worth, parsing 10k items with mixed exceptions (swallowed) took 0.1119411ms, with no exceptions was 0.000856ms. imho in the end it's just good to know, main thing should be limit exceptions thrown if warranted, handle any expected "condition" instead, developers tend to overuse exceptions – Jason Apr 06 '18 at 16:17
  • I don't disagree that unwinding an exception's stack is an expensive operation, especially compared to parsing an int and throwing an exception. Why would you need the stack of 5k to 50k near identical exceptions? There might be a point where exceptions are being overused, but otherwise replacing them with tuples or returned booleans is a bad idea. – Stephen Turner Apr 09 '18 at 08:53
0

Using exceptions has the advnatage that you can potentially(*) distinguish from the reason more easily than the proposed tuple return value. To figure out what kind of thing is at fault when using the tuple, you have to interpret the string value, which is error prone. Using exceptions, you can determine the kind of error based on the type of the exception.

(*) depends on how the exceptions are used -- if you are throwing a generic Exception all the time, it won't be different

Of course you could use an integer in the tuple to indicate the type of the problem, but a numeric value for the error type is not as descriptive as an Exception type (again, assuming you are not using a general type, like Exception).

Attila
  • 28,265
  • 3
  • 46
  • 55
-1

What I did once was return null on success or error message on failure. Exception was inappropriate as failure was likely and other developers like to run with "Break when exceptions are thrown"

String Foo()  
{  
    if(!DoHardWorkA()) return "cannot do hardwork A";
    if(!DoHardWorkB()) return "cannot do hardwork B";

    return null;
}
Joshua
  • 40,822
  • 8
  • 72
  • 132