Newly discovered hidden benefits of inline variables in Delphi by Darian Miller

FireWind

Свой
Регистрация
2 Дек 2005
Сообщения
1,957
Реакции
1,199
Credits
4,009
Newly discovered hidden benefits of inline variables in Delphi
Darian Miller

[SHOWTOGROUPS=4,20]
Newly discovered hidden benefits of inline variables in Delphi
Darian Miller
ec6db6_10452bd5b0d644b3a476f7fe611199e5~mv2.webp



To be honest, I have always regarded the Для просмотра ссылки Войди или Зарегистрируйся feature as something that I would never use, except perhaps variable initialization as that is obviously a very cool thing which makes your code much more readable as in the example below, but I obviously won't like changing!

Код:
procedure Test1; // multiple inline declarations (symbols declared when used)
begin
  var I: Integer := 22;
  var J: Integer;
  J := 22 + I;
  var K: Integer := I + J;
  ShowMessage (K.ToString);
end;


One of the core requirements of Pascal is the strict typing of your variables required at the top of all your methods and to set that aside simply for variable initialization gives me great pause. However, there's a related improvement in 10.3 that I had ignored: type inference of inline variables which includes a very nice hidden benefit. (It was hidden to me until today when it was revealed by Stefan Glienke in a Для просмотра ссылки Войди или Зарегистрируйся.)

Type inference is kinda cool, as you don't have to tell the compiler that your loop variable is an integer or another obvious type. And once you pair that feature with the limited scope of inline variables to the particular block of code in which they are declared, then you get the additional benefit of a compiler error (instead of a warning) if you reference your loop variable outside the block. So, I will eventually use inline variables for loops...but I probably won't like changing!

Код:
for var Item in Collection do
  begin
    //Item is usable here
  end;
//References to the Item variable generates compiler error here


But what Stefan mentioned today blew my mind. He gave an additional reason to like inline variables: "often enough you have to add units to a uses clause just to declare a variable of a type that is the return of a method of some other type being used - with type inference that becomes unnecessary." At first, I didn't quite understand what he just wrote. I then re-read it and understood what he wrote but told myself that he was obviously wrong. I then said, well, this is Stefan so I should probably give him the benefit of the doubt in this area, so I immediately loaded up Delphi 10.3.3. I then typed in some test code, being fully confident within myself that it would absolutely fail to compile. To my disbelief, I hit F9 and it ran without errors or warnings. I now sat there for a moment thinking about the consequences and then said I will use this feature and I will like it!

Now if you are pulling off your best Winnie the Pooh impression, tapping your head like I was, saying to yourself "Think, Think, Think..." let's go over a simple example.

Start a new VCL project and add a TMemo and a TButton to the form as you've undoubtedly have done countless of times. Now double-click the button to add an OnClick event with the intent to fill the memo with a list of files from a particular directory (just for simple example code.)

Then add System.IOUtils to the Uses clause in the implementation section of the unit to give your code access to the TDirectory class. Then type some code looking similar to this in the new Click event:

Код:
procedure TForm1.Button1Click(Sender: TObject);
var
  vFileNameList:TStringDynArray;
  i:Integer;
begin
  vFileNameList := TDirectory.GetFiles('C:\', '*.*');
  for i := Low(vFileNameList) to High(vFileNameList) do
  begin
    Memo1.Lines.Add(vFileNameList[i]);
  end;
end;


Others will code it like this:

Код:
procedure TForm1.Button1Click(Sender: TObject);
var
  vFileNameList:TStringDynArray;
  vFileName:string;
begin
  vFileNameList := TDirectory.GetFiles('C:\', '*.*');
  for vFileName in vFileNameList do
  begin
    Memo1.Lines.Add(vFileName);
  end;
end;

In any case, once you hit F9 to run you will get an error similar to:
[dcc32 Error] Unit1.pas(30): E2003 Undeclared identifier: 'TStringDynArray'
This is definitely a common annoyance. To fix, you go back to the Uses clause and add System.Types. Now when you hit F9, your code should run just fine.

Now, what if you leveraged the benefits of Для просмотра ссылки Войди или Зарегистрируйся? Continue with the same example VCL project and simply re-code your Click event to use inline variables, similar to this:

Код:
procedure TForm1.Button1Click(Sender: TObject);
begin
  var vFileNameList := TDirectory.GetFiles('C:\', '*.*');
  for var vFileName in vFileNameList do
  begin
    Memo1.Lines.Add(vFileName);
  end;
end;

Now hit F9 and it will run as expected (yes, it looks a little odd with the inline variables!) But the somewhat hidden benefit is that you no longer need System.Types in your uses clause. Try it - remove System.Types and you'll find that it compiles and runs just fine when using the type inference feature of inline variables. I honestly didn't think it would work.

In my head, below is what I was actually typing and this does indeed fail to compile unless System.Types is in the Uses clause. (To me, it's a little counter-intuitive to leave out the type declaration of inline variables.)

Код:
procedure TForm1.Button1Click(Sender: TObject);
begin
  var vFileNameList:TStringDynArray := TDirectory.GetFiles('C:\', '*.*');
  for var vFileName:string in vFileNameList do
  begin
    Memo1.Lines.Add(vFileName);
  end;
end;

Now ask yourself this simple question - how many times have you hit F9 and had to go back and find which unit to add just for one simple new variable that you just added to your code? I know it's happened more than a few times to me.

Now, even more impactful is that I have often reorganized code to lump types into a single unit mainly to reduce this very specific pain point. (Have you?) Therefore, inline variables not only reduce the immediate common annoyance as demonstrated above, they also help to reduce the desire to have larger units, which is likely a much more important incentive. Suffice to say, after giving it a little more thought, I've gone 180 degrees on inline variables usage in a one day based on a single comment that gave me pause. (Thanks Stefan!)

Of course, there's a definite problem with inline variables in Delphi 10.3.3 as unfortunately the IDE tooling doesn't properly support it. Code Insight and Error Insight both fail to properly recognize inline variables. But, no worries, Для просмотра ссылки Войди или Зарегистрируйся is right around the corner and it introduces an Error Insight Which Shall Not Suck
ec6db6_64c648d624b34b219a7ca9bce43fba12~mv2.webp



One of the very first things I have done with every new install of Delphi for a number of years is to disable the Error Insight option. It was simply wrong so often that it was more of a distraction than a benefit. With the upcoming support of the Для просмотра ссылки Войди или Зарегистрируйся, allowing the back end tools to notify the front-end code editor which code has errors and which doesn't. In Delphi 10.4, Error Insight will produce the exact same errors as the compiler does - simply meaning that it will work as expected 100% of the time....thus, it Shall Not Suck.

For more information, see David Millington's recent Для просмотра ссылки Войди или Зарегистрируйся. And, as they are saying, it's time to Get Excited about Delphi 10.4! Leveraging inline variables with an improved Code Insight, a much improved Error Insight, and some new internal insight to the benefits of inferred typing.
[/SHOWTOGROUPS]