Created
February 6, 2018 23:03
-
-
Save DelphiWorlds/1b13a88bb9c886a75b8eadb26901ff46 to your computer and use it in GitHub Desktop.
Distinction between causing and raising an exception, and try..finally safety
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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