The Right Approach to Exceptions
jimhewes at gmail.com
Sun Feb 19 12:31:16 PST 2012
On 2/19/2012 3:40 AM, deadalnix wrote:
> Well, I think you are messing up between contract and exception. Wrong
> parameters is a contract problem, not an exceptionnal situation.
Here is an example of what I'm getting at. Let's say you have a
Dictionary class. This class has a function called Get(key) which will
retrieve an item from the dictionary. But the item may not be found in
the dictionary. Should you throw an exception if the item isn't found.
Well, the “exceptional situations” crowed will say that since it's
common for an item to not be found, this isn't an exceptional situation
and you should therefore not throw an exception. An item not being in
the dictionary is part of the normal program flow and you don't want
exceptions firing off during normal program flow. But then what? You
return false or a NotFound error code? But then do I need to check for
this error every time I extract an item from the dictionary? You're
losing the benefit that exceptions bring.
Here is the way I'm thinking exceptions are related to contracts. I
think you need to better define what the function is expected to do and
not do in order to know when an exception is thrown. I can define
Get(key) in two ways. I can declare that the item associated with 'key'
must exist in the dictionary. Therefore if the item is not in the
dictionary then the contract of the caller is not fulfilled, it's an
error, and an exception is thrown. But how is the caller supposed to
know an item is not in the dictionary before calling Get(key) and thus
avoid exceptions? One solution is to provide a second function called
ItemExists(key). You can call this before calling Get(key).
ItemExists(key) is then defined to return true of false depending on
whether the item is in the dictionary. It doesn't throw an exception if
the key doesn't exist because it's main job is to determine if the key
exists or not.
Obviously this causes bad performance because the item must be looked up
twice each time you retrieve it. So the second way to deal with the
problem is to define the contract of Get(key) differently such that the
argument 'key' is not required to exist in the dictionary. To
distinguish this function, call it TryGet(key). This will return false
if the item is not in the dictionary. (But it may still throw an
exception for other errors such as a null argument or whatever.)
So I think whether an exception is thrown depends on the function's
contract. It does not have to do with errors being “exceptional” or not.
Perhaps I should not even be using the term contract at all and should
just say that functions need to be clearly defined. I don't know. I'm
always open to learning more about exceptions to create better designs,
which is partly why I jumped on this topic (and now so selfishly pulled
it off topic). If anyone thinks I'm totally off base then tell me why
and I might learn something.
By the way, the example I was using actually exists in the .NET
Framework and uses both approaches. Look at the
System.Collections.Generic.Dictionary class. Instead of Get, ItemExists
and TryGet, the functions are named respectively:
Item (bracket syntax this)
The KeyNotFoundException is only thrown by the Item property. So, I just
put this out there as an example of when a seemingly “non-exceptional”
case can use exceptions depending on the contract of the function.
More information about the Digitalmars-d