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 disadvantage开发者_Python百科s?
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."
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.
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.
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.
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);
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.
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
精彩评论