Monday, August 11, 2008

Exceptions, return value checking, pre- and postconditions

I never actually worked with Eiffel but when I first heard about it (that is some decades ago, really...) there is one concept that I remembered for the whole of my professional career and I adhered as a 'coding principle' in my own code. I'm talking about the concept of pre- and postconditions.

As I remember it in Eiffel you can write your method/function/procedure (whatever it's called) and define some conditions that must be met before and at the end of that. E.g. you could define a precondition for a function that will ensure that a passed parameter is larger than zero. For the remainder of your code you can assume that the passed parameter IS larger than zero.

Now, whenever I write a function/method I do the same. E.g. if I do a typecast somewhere in my code, I try to check for the correctness of the object that is typecast as soon as possible in my code. Something like this (from the top of my head, forgive me typos, simplicity of the examples and such):

procedure Foo(const aControl: TWinControl);
begin
// Beginning of preconditions
if not Assigned(aControl) then exit;
if not (aControl is TEdit) then exit;
// End of preconditions

// You can now safely execute the code below
TEdit(aControl).Text := 'foo';
end;

The above works fine if there actually is something that you can check at the beginning of the code. But, what if you cannot, until right before you start to use an object? Like below:

procedure Foo();
var
aControl: TEdit;
begin
(**
A Whole Bunch of Code Here
**)
aControl := MyFunctionSomeWhereThatReturnsTEdit();
if (not Assigned(aControl)) then exit;
if (not (aControl is TEdit)) then exit;
aControl.Text := 'foo';
end;

In this case you would add your check just below the aControl := line. And if you have a lot of those, your code can become complex and hard to read. And you would end up with a lot of not so interesting code which in the end doesn't really matter to your program.

Now, until now I used assertions for this type of checking, mainly because you can turn those off easily using a compiler switch.

aControl := MyFunctionSomeWhereThatReturnsTEdit();
Assert(Assigned(aControl));
Assert(aControl is TEdit);

But, of course, this doesn't work if the 'failing conditions' might be ligitimate circumstances in your code. So, for those types of checks I used the if..then constructions from the first example.

But, I am now thinking that I'd better use exceptions for this. Currenlty I only use exceptions for 'error circumstances' and never really used them as a way to signal pre- and postconditions. It would go like this:

procedure Foo();
var
aControl: TEdit;
begin

try
(**
A Whole Bunch of Code Here
**)
aControl := MyFunctionSomeWhereThatReturnsTEdit();
aControl.Text := 'foo';

except
on E: MyConditionException do
begin
// This will be an exception raised from within MyFunctionSomeWhereThatReturnsTEdit
end;
on E: Exception do // There probably are seperate exceptions for nil and class type mismatch exceptions, but this will do for the example
begin
// Something else should be done.
end;
end;
end;


This will work (and I think I will give it a go in some small program to see if it works our for my style of programming), but I'd much rather see the concept of pre- and postconditions introduced in Delphi.

What do you think? If..Then constructions at the beginning? Exceptions? Push Embarcadero for pre-/postconditions? Something else?

Please let me know; I'm really curious.

Bye,
Bart