Tuesday 30 October 2018

Inline declaration of variables

There will be a new interesting feature in the upcoming Delphi 10.3 Rio: inline declared variables. I have permission to blog a little about it.

Marco Cantù already wrote about this new feature. I am not going to explain it, as I think he already did a good job doing that.

I just want to demo one of the main advantages of them: they are block-local, i.e. they are initialized at the point of declaration and are finalized at the end of the begin-end block they were declared in. This means that an interface or other managed variable declared that way is finalized at the end of the block.

To demo this, I made a little talkative interface, which tells us what it is doing, and when it is being created and being destroyed. It also has a name. Here is the code:

type
  ITalking = interface
    procedure Talk;
  end;

  TTalking = class(TInterfacedObject, ITalking)
  private
    FName: string;
  public
    constructor Create(const Name: string);
    procedure Talk;
    destructor Destroy; override;
  end;

implementation

{ TTalking }

constructor TTalking.Create(const Name: string);
begin
  FName := Name;
  Writeln(Format('Creating %s', [Name]));
end;

destructor TTalking.Destroy;
begin
  Writeln(Format('Destroying %s', [FName]));
  inherited;
end;

procedure TTalking.Talk;
begin
  Writeln(Format('Hi, %s talking here!', [FName]));
end;

Demo

And here is the demo:

procedure Test;
begin
  var I1: IInterface := TTalking.Create('One'); // one way to declare an interface
  Writeln('// Before block');
  Writeln;
  begin
    Writeln('// Before first inline var...');
    var I2 := TTalking.Create('Two') as ITalking; // the other way: type inference
    I2.Talk;
    var I3: ITalking := TTalking.Create('Three');
    I3.Talk;
    Writeln('// Do something useful here');
  end;
  Writeln;
  Writeln('// After block');
  var I4: ITalking := TTalking.Create('Four');
  Writeln('// After declaration of Four');
end;

And the output is:

Creating One
// Before block

// Before first inline var...
Creating Two
Hi, Two talking here!
Creating Three
Hi, Three talking here!
// Do something useful here
Destroying Three
Destroying Two

// After block
Creating Four
// After declaration of Four
Destroying Four
Destroying One

As you can see, the two interfaces declared and initialized inside the inner block are destroyed at the end of the same block. The interfaces declared and initialized in the outer block are destroyed at the end of the function.

This creates some interesting opportunities, e.g. for RAII using interfaces, or other auto-finalized structures. RAII doesn't have to be used for smart pointers. It can also be used for many other things, like locks, mutexes, temporary changes of cursors, etc. i.e. anything that is changed temporarily and restored at the end of a certain period. Just declare the RAII object inside the block and the change will be restored at the end of it. Just pass it an anonymous function to tell it what it must do at the end of the block (or in case of an exception).

But more about this later. FWIW, I also like the type inference, or that it can be used for for-loops.

2 comments:

  1. I think you meant I2.Talk instead of I.Talk and I3.Talk instead of I2.Talk.

    Up to now those begin..end blocks without any if clause or anything normally did not change anything. So they could be removed without problems. Important to remember that this is not the case anymore starting with 10.3.

    ReplyDelete
    Replies
    1. begin..end blocks without any if, else, while or for are probably very seldom, so I don't think it will make a big difference.

      Delete