The other day my colleague was probing around whether to return a value or to throw an exception in general scenarios and I initially told him that throwing exceptions almost always incur performance costs as compared to a returned value. We had then left the discussion there itself but yesterday while coding one of my CR, I stumbled upon this very good example where I can throw some light on fetching the return versus throwing exceptions. The example is ‘parsing an integer from a string’. There are 3 ways we can do that (or more perhaps).
Int32 Int32.Parse(string input);
Int32 Convert.Int32(string input);
Boolean Int32.TryParse(string input, out Int32 result);
Performance wise in case of bad input,
1) Int32.TryParse is the fastest as it checks for the probable failure cases before hand and returns a Boolean value indicating the result. Also there is an out parameter which contains the actual result data. It avoids exception routines
2) Convert.ToInt32 is relatively faster than normal Parse and slower than TryParse because it internally checks just for null condition and returns zero if null, otherwise calls Int32.Parse only.
3) Int32.Parse is the slowest because of the exception handling routines
However, the answer to above dilemma is not always as simple as it seems. It depends on situation (a typical answer ‘It depends’ whenever you ask anything to an architect’:)). But jokes apart, yes the decision to go for pre-emptive multiple if checks (Tester-Doer pattern) or simple try/catch blocks depends on your program’s trends. General rule of thumb is, leave extremely rare scenarios and really unlikely stuff to exception handling as the cost of if check will be greater for most of the part. If your method fails frequently or bound to fail much often (for e.g., using dictionary), go for implementing a Tester-Doer pattern.
Also, do not forget that Tester-Doer pattern may need careful implementation in case of multi-threading where there is a possibility of another thread changing the shared state in between. For e.g.,
If(Cache["key"] != null)
String str = Cache["key"].ToString();
In the above code, in between, another thread removes the data from cache and then an exception will be thrown.
It can be prevented as,
Object key = Cache["Key"];
If(key != null)
String str = key.ToString();