Skip to content

Instantly share code, notes, and snippets.

@DelphiWorlds
Created February 6, 2018 23:03
Show Gist options
  • Save DelphiWorlds/1b13a88bb9c886a75b8eadb26901ff46 to your computer and use it in GitHub Desktop.
Save DelphiWorlds/1b13a88bb9c886a75b8eadb26901ff46 to your computer and use it in GitHub Desktop.
Distinction between causing and raising an exception, and try..finally safety
type
TSomeObject = class(TObject)
public
procedure DoSomething;
end;
TFaultyObject = class(TObject)
private
FStrings: TStrings;
public
constructor Create;
destructor Destroy; override;
procedure DoSomething;
end;
{ TSomeObject }
procedure TSomeObject.DoSomething;
begin
Sleep(0);
end;
{ TFaultyObject }
constructor TFaultyObject.Create;
begin
inherited;
FStrings := TStringList.Create;
end;
destructor TFaultyObject.Destroy;
begin
FStrings.Free; // <------- This CAUSES an exception if DoSomething has been called
// if Now + 1 > Now then
// raise Exception.Create('Exception!'); // <---- This RAISES an exception. Convention says: a "no-no" in destructors
inherited;
end;
procedure TFaultyObject.DoSomething;
begin
FStrings.Free; // <---- Whoops!
end;
procedure TryFinally1;
var
LObject1: TFaultyObject;
LObject2: TSomeObject;
begin
LObject2 := TSomeObject.Create;
try
LObject1 := TFaultyObject.Create;
try
LObject1.DoSomething;
LObject2.DoSomething;
finally
LObject1.Free; // <----- when the finally is reached, this code is GUARANTEED to execute
end;
finally
LObject2.Free; // <----- when the finally is reached, this code is GUARANTEED to execute
end;
end;
procedure TryFinally2;
var
LObject1: TSomeObject;
LObject2: TFaultyObject;
begin
LObject1 := nil;
LObject2 := nil;
try
LObject1 := TSomeObject.Create;
LObject2 := TFaultyObject.Create;
LObject1.DoSomething;
LObject2.DoSomething;
finally
{ If either Create fails, LObject2 is guaranteed to be nil.
And Free is safe from a nil reference. }
LObject2.Free;
{ Similarly, if LObject1's Create fails, Free is still safe.
And if LObject1's create succeeds, but LObject2's fails: LObject1 refers to a valid
object and can be destroyed.
**** HOWEVER **** if LObject2.Free CAUSES (or raises, which you would hope it would not) an exception, LObject1.Free is NEVER free'd, thus
this scenario is LESS SAFE than TryFinally1
}
LObject1.Free; // <----- This code is ***NOT GUARANTEED*** to execute (and IS NOT in this example)
end;
end;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment