The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Email Subscription

    Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 717 other subscribers

Archive for August 7th, 2014

Delphi: a memento that executes any code at end of method

Posted by jpluimers on 2014/08/07

Following up on yesterdays Delphi: using IInterface to restore cursor at end of mehod (prelude to a memento that executes any code at end of method) here is the memento I meant.

They are based on anonymous methods, which in Delphi are closures: they capture location.

The location is kept just as long as needed, based on a well known Delphi reference counting mechanism: interfaces. The same one I used for the TTemporaryCursor class (and one of the reasons the TTemporaryCursor will keep functioning).

My goal was to simplify code like this:

[sourcecode language=”pascal”]
procedure TTemporaryCursorMainForm.TemporaryCursorClassicButtonClick(Sender: TObject);
var
Button: TButton;
SavedCursor: TCursor;
SavedEnabled: Boolean;
begin
Button := Sender as TButton;
SavedEnabled := Button.Enabled;
try
Button.Enabled := False;
SavedCursor := Screen.Cursor;
try
Screen.Cursor := crHourGlass;
Sleep(3000);
finally
Screen.Cursor := SavedCursor;
end;
finally
Button.Enabled := SavedEnabled;
end;
end;
[/sourcecode]

Into this:

[sourcecode language=”pascal”]
procedure TTemporaryCursorMainForm.TemporaryCursorMementoButtonClick(Sender: TObject);
var
Button: TButton;
SavedEnabled: Boolean;
begin
TTemporaryCursor.SetTemporaryCursor();
Button := Sender as TButton;
SavedEnabled := Button.Enabled;
TAnonymousMethodMemento.CreateMemento(procedure begin Button.Enabled := SavedEnabled; end);
Button.Enabled := False;
Sleep(3000); // sleep 3 seconds with the button disabled crHourGlass cursor
// Delphi will automatically restore the cursor
end;
[/sourcecode]

We’ve already seen one of the try…finally…end blocks vanish by using TTemporaryCursor. Now lets look at TAnonymousMethodMemento:

[sourcecode language=”pascal”]
unit AnonymousMethodMementoUnit;

interface

uses
System.SysUtils;

type
IAnonymousMethodMemento = interface(IInterface)
[‘{29690E1E-24C8-43A5-8FDF-5F21BB32CEC2}’]
end;

TAnonymousMethodMemento = class(TInterfacedObject, IAnonymousMethodMemento)
strict private
FFinallyProc: TProc;
public
constructor Create(const AFinallyProc: TProc);
destructor Destroy; override;
procedure Restore(const AFinallyProc: TProc); virtual;
class function CreateMemento(const AFinallyProc: TProc): IAnonymousMethodMemento;
end;

implementation

{ TAnonymousMethodMemento }
constructor TAnonymousMethodMemento.Create(const AFinallyProc: TProc);
begin
inherited Create();
FFinallyProc := AFinallyProc;
end;

destructor TAnonymousMethodMemento.Destroy;
begin
Restore(FFinallyProc);
inherited Destroy();
end;

class function TAnonymousMethodMemento.CreateMemento(const AFinallyProc: TProc): IAnonymousMethodMemento;
begin
Result := TAnonymousMethodMemento.Create(AFinallyProc);
end;

procedure TAnonymousMethodMemento.Restore(const AFinallyProc: TProc);
begin
AFinallyProc();
end;

end.
[/sourcecode]

Like TTemporaryCursor, I’ve kept it self-contained.

It uses a TProc parameter – a parameterless anonymous method – called AFinallyProc that needs to be executed right before the memento goes out of scope.

It can be called like any method, as to the compiler it is a method.

–jeroen

Posted in Delphi, Delphi 2009, Delphi 2010, Delphi XE, Delphi XE2, Delphi XE3, Delphi XE4, Development, Software Development | 11 Comments »