When the caller is expecting that a condition may occur, it is best to inform the caller of that condition via return code. When a condition occurs which the caller is not expecting, it is best to inform the caller via an exception.
Because most languages do not provide any means for a called routine to 'magically' know what the caller is expecting, the only way a routine will know what the caller is expecting is for the caller to tell it. There are a few patterns I would suggest:
- Have both a "DoSomething" method and a "TryDoSomething" method. The former will throw an exception if it cannot achieve the intended objective, while the latter will throw an exception.
- Use a single method with a Boolean or enumeration parameter which indicates whether a routine should behave as a "Try" or "Do" method. This is often useful in conjunction with small "DoSomething" and "TryDoSomething" methods which call the combined routine passing an appropriate parameter value. Although many implementations would make the combined routine protected, having it public may allow it to be called from another "Do or Try" routine, passing the ThrowOnFailure value from the other routine to the inner routine.
- Have a single method with overloads that include or do not include a 'ref' parameter that will be used to indicate success or failure. The version of the function with the parameter will use it to indicate failure (without throwing an exception), while the overload without it will throw an exception in the failure case.
- Have a single method with a delegate which should be invoked in case of failure. Conceptually, this is an even better approach than passing a Boolean parameter, since the delegate can not only throw an exception the invoking code will be prepared to catch; it can also set flags or take other action so when the invoking code catches the exception, it can be sure what really happened. Unfortunately, figuring out the optimal form for the delegate, including what parameters it should take, is tricky.
Approach #1 seems to be Microsoft's recommended pattern, though I think #2 can help avoid duplicate coding. Approach #3 would be similar to a convention used in Borland Pascal some years back, that worked out relatively intuitively. Approach #4 would seem the nicest, but I haven't figured out how best to do it practically.