7

I've read in The Pragmatic Programmer, and in some other articles (including one from Joel Spolsky), that you should only throw Exceptions in exceptional cases. Otherwise, you should return an error.

Sometimes it is possible (returning -1, -0, or a positive number, for example), but there are other cases where that is not possible. I mean, if you are returning a class, you can always return null, but I think the idea is to return something that will alert the caller what happened.

If I always return null, I don't think it is useful to say: If this method returns null, it could be because of A, B, C, D, or E

So, how exactly can this be implemented in C#?

Edit:
Hours after I posted this question, I saw another question here where the question itself was about wheter the code posted in was a good practice or not.

I see that is another way of doing what I was asking here. Here is the link:

Generic property disadvantages?

Community
  • 1
  • 1
Oscar Mederos
  • 29,016
  • 22
  • 84
  • 124
  • 3
    The problem is that it's wishful thinking to assume that every consumer will do the return-code checking that they're supposed to. And that means that they won't detect when your method has failed. Exceptions make the API *much* easier to use. Always throw them when a method has failed to complete the task it was called to do. The point is *never* to use them for flow control. – Cody Gray - on strike May 07 '11 at 08:24
  • @CodyGray That's how I've been doing this, and I totally agree with you, but I couldn't resist to ask when I read the same on two different (and good) places in the same day: The Pragmatic Programmer and Joel On Software blog. – Oscar Mederos May 07 '11 at 08:26
  • Fair enough. I'd have posted a more comprehensive answer, but there's already one accepted and it looks like the top voted answers pretty well cover it. Exceptions are a very misunderstood topic, and you'll see a lot of programmers familiar with how they work in C++ that erroneously discourage their use in C#. The [.NET Framework Design Guidelines](http://www.amazon.com/gp/product/0321545613/ref=pd_lpo_k2_dp_sr_1?pf_rd_p=486539851&pf_rd_s=lpo-top-stripe-1&pf_rd_t=201&pf_rd_i=0321246756&pf_rd_m=ATVPDKIKX0DER&pf_rd_r=1BPRHRK198V2SDKG600W) are an excellent resource. – Cody Gray - on strike May 07 '11 at 08:31
  • @CodyGray Any other reference to keep reading about *Exceptions are a very misunderstood topic*? – Oscar Mederos May 07 '11 at 08:35
  • Hard to filter the misinformation from valid suggestions. That makes online resources very tricky. The FDG are the best reference I know of. You'll also find lots of programmers who are applying their knowledge of C++ exceptions to C#, or pragmatically-speaking, say that something is necessary only because they are ignorant of alternatives. I'm often arguing that catching and rethrowing exceptions simply to log them is almost always the wrong idea. It offends people who think I have no "real world experience". Something like the `AppDomain.UnhandledException` event is always a better call. – Cody Gray - on strike May 07 '11 at 08:48
  • I've written several answers on exception handling best practices. [This one](http://stackoverflow.com/questions/4827628/main-method-code-entirely-inside-try-catch-is-it-bad-practice/4827646#4827646) immediately comes to mind. And [this one](http://stackoverflow.com/questions/5919940/a-question-about-exception-in-c) just moments ago. There are probably others as well. And then the really tricky part is understanding the limitations of Windows call stacks. Russinovich's texts are imperative for that. – Cody Gray - on strike May 07 '11 at 08:49
  • Eric Lippert has one good article on this available online. [Read it here](http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx). The thing to pay attention to there is what he calls "vexing exceptions". These are the ones *most* people are talking about when they tell you not to throw exceptions. Otherwise, fail fast is about the best strategy around. You want to fix whatever is causing the exception during debugging, or otherwise do nothing and let the program fail. If the state got corrupted and you can't fix it, you're better off crashing. – Cody Gray - on strike May 07 '11 at 08:54
  • @CodyGray Great! I will take a look at them :) You should merge those 5 comments and place them into an answer ;) Thank you. – Oscar Mederos May 07 '11 at 08:57
  • Throwing exceptions is also nice in that you can provide error messages as strings rather than return error codes that you have to look up the meaning of. – Matthew Lock Jan 15 '14 at 03:11

7 Answers7

12

A far better rule for when to throw exceptions is this:

Throw an exception when your method can't do what its name says that it does.

A null value can be used to indicate that you've asked for something that does not exist. It should not be returned for any other error condition.

The rule "only throw exceptions in cases that are exceptional" is unhelpful waffle IMO since it gives you no benchmark to indicate what is exceptional and what isn't. It's like saying "only eat food that is edible."

jammycakes
  • 5,780
  • 2
  • 40
  • 49
  • I really like this answer as it provides the least ambiguous description by quite a margin. – Darren Lewis May 07 '11 at 08:35
  • 1
    To this I would add that if method called TryXXX can't do XXX for a reason that the caller is likely anticipating, it tried to do XXX (meaning it did what its name said, and should thus not throw an exception); if the failure is one a caller likely isn't prepared for, it may be good to throw an exception, taking the view that something prevented it from giving XXX a "good try". – supercat May 12 '11 at 17:10
10

By using numbers you're directly contradicting what Microsoft recommend with Exceptions. The best source of information I've found that de-mystifies the whole subject, is Jeffrey Richter's CLR via C# 3. Also the .NET Framework Guidelines book is worth a read for its material on Exceptions.

To quote MSDN:

Do not return error codes. Exceptions are the primary means of reporting errors in frameworks.

One solution you could adopt is an out parameter, which gets the result. Your method then returns a bool much like a lot TryParse methods you meet in the framework.

Chris S
  • 64,770
  • 52
  • 221
  • 239
8

One example to consider is int.TryParse. That uses an out parameter for the parsed value, and a bool return value to indicate success or failure. The bool could be replaced with an enum or a more complex object if the situation merits it. (For example, data validation could fail in ways that would really warrant a collection of failures etc.)

An alternative to this with .NET 4 is Tuple... so int.TryParse could have been:

public static Tuple<int, bool> TryParse(string text)

The next possibility is to have an object encapsulating the whole result, including the failure mode if appropriate. For example, you can ask a Task<T> for its result, its status, and its exception if it failed.

All of this is only appropriate if it's not really an error for this to occur, as such. It doesn't indicate a bug, just something like bad user input. I really don't like returning error codes - exceptions are much more idiomatic in .NET.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Ack... I don't think I would want to spend much time working with a public API sprinkled with tuples. – Dave Ziegler May 06 '11 at 22:12
  • 1
    @Jack - That sort of API would work great if the language supported tuple expansion *(EX: F#)*. – ChaosPandion May 06 '11 at 22:14
  • @ChaosPandion: Yes, it would definitely work better with language support. At which point I think it would be infinitely preferable to the `out` parameter, personally. – Jon Skeet May 06 '11 at 22:16
  • How do I know what the int and bool represent in the example above? Somewhat obvious in this situation I suppose, but anything more complex might be nasty. I'm not a fan of the out param thing though either. – Dave Ziegler May 06 '11 at 22:16
  • 1
    @Jack: How do you know what they represent when one is a return value and one is an out parameter? It's true that using `Tuple` is a pretty ad-hoc way of combining multiple return values, but I believe in practice it would be pretty clear, especially if language support let you write `var (value, ok) = int.Parse(text);` to declare two variables, `value` (an `int`) and `ok` (a `bool`). – Jon Skeet May 06 '11 at 22:17
  • Not a fan of either option, personally. I would rather have a well-defined object returned if I need anything more than say a bool or an int. Personal preference. :) – Dave Ziegler May 06 '11 at 22:21
  • That's something I really like from Python :/ – Oscar Mederos May 06 '11 at 22:33
  • @Jack: I think for the simple success/failure + result, a tuple would be okay. For something more complex, a separate type is appropriate. – Jon Skeet May 06 '11 at 22:33
  • Why not have `TryParse` simply return a `int?`? – Thom Smith Jul 05 '12 at 13:31
  • @ThomSmith: Yes, that's another option - it doesn't work for similar situations where `null` would be a valid result though. – Jon Skeet Jul 05 '12 at 13:56
1

Microsoft has documented best practices for error handling in .NET

http://msdn.microsoft.com/en-us/library/8ey5ey87(VS.71).aspx

I'd recommend following those guidelines, as it's the standard for .NET and you'll have far fewer conflicts with other .NET developers.

(Note, I realize I posted to an older link, but the recommendations have remained.)

I also realize that different platforms vary on how they view proper error handling. This could be a subjective question, but I'll stick to what I said above - when developing in .NET follow the .NET guidelines.

David
  • 72,686
  • 18
  • 132
  • 173
1

You may want to consider following the TryXXX pattern with a couple simple overloads for the client to consider.

// Exception
public void Connect(Options o); 

// Error Code
public bool TryConnect(Options o, out Error e); 
ChaosPandion
  • 77,506
  • 18
  • 119
  • 157
1

Joel Spolsky is wrong. Returning status via error/return codes means that you can't trust the results of any method invocation — the returned value must be tested and dealt with.

This means that every method invocation returning such a value introduces at least one choice point (or more, depending on the domain of the returned value), and thus more possible execution paths through the code. All of which must be tested.

This code, then, assuming that the contract of Method1() and Method2() is to either succeed or throw an exception, has 1 possibly flow through it.:

foo.Method(...) ;
bar.Method(...) ;

If these methods indicate status via a return code, it all of a sudden gets very messy very quickly. Just returning a binary value:

bool fooSuccess = foo.Method(...);
if ( fooSuccess )
{
  bool barSuccess = bar.Method(...);
  if ( barSuccess )
  {
    // The normal course of events -- do the usual thing
  }
  else
  {
    // deal with bar.Method() failure
  }
}
else // foo.Method() failed
{
  // deal with foo.Method() failure
}

Returning status codes rather than throwing exceptions

  • complicates testing
  • complicates comprehension of the code
  • will almost certainly introduce bugs because developers aren't going to capture and test every possible case (after all, how often have you actually seen an I/O error occur?).

The caller should be checking to make sure things are hunky-dory prior to invocation the method (e.g., Check for file existence. If the file doesn't exist, don't try to open it).

Enforce the contracts of your method:

  • Preconditions. Caller warrants that these are true prior to method invocation.
  • Postconditions. Callee warrants that these are true following method invocation.
  • Invariant conditions. Callee warrants that these are always true.

Throw an exception if the contract is violated. Then throw an exception if anything unexpected happens.

You code should be a strict disciplinarian.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
0

I return such values as null references or negative index values only if I'm sure that there will be no misunderstanding when the code will be used by some other developer. Something like LINQ function IEnumerable<T>.FirstOrDefault. IEnumerable<T>.First throws an exception for empty collections, because it is expected that it will return the first element, and the empty collection is an exceptional case

Marek
  • 1,688
  • 1
  • 29
  • 47