在Delphi中使用内联变量(inline variables) 的5个理由

内联变量声明(Inline Variable Declaration)是Delphi Rio 10.3中引入的功能。它是什么?

简而言之,可以在代码的任何行中声明一个变量。也就是说,您可以在begin..end块中以这种方式声明变量:

procedure Test;
begin
  var I: Integer;
  I := 22;
  ShowMessage (I.ToString);
end;

许多人已经了解了此功能的工作原理,但不了解为什么它很有趣。在本文中,我将向您展示此新功能,重点介绍其带来的优势。

1.整理您的代码

该变量只能从声明点访问。对于许多人来说,这更好地组织在一个大的方法中的代码,因为它可以更好地了解那里正在使用的变量。考虑以下代码:

procedure Test;
var 
  A, B, C, D, E: Integer;
  Found, Done, Excluded: Boolean;
  Text: string;
begin
   // many
   // lines
   // of
   // code
end;

了解所有这些变量的使用位置,初始化时间,是否在之前设置了值等可能令人困惑。在下面的代码中,我们知道Text变量例如不存在于代码的开头,并且仅在结尾使用。在该部分代码之前,没有代码会更改其值:

procedure Test;
begin
   var A, C: Integer;
   // We cannot use Text here
   // lines
   // of
   // code
   
   var Text: string;
   // Text can only be used here
end;

2.尽量减少错误

您是否曾经做过这样的事情:

procedure Test;
var I: Integer;
begin
  for I := 0 to Count - 1 do
    Process;
  DoSomethingWithI(I);
end;

也就是说,在循环结束后使用for变量。这是不安全的,尽管编译器对此发出警告,但许多人忽略了它。通过声明for变量内联,它将仅在for内有效,而在块外使用它将导致编译错误:

procedure Test;
begin
  for var I: Integer := 0 to Count - 1 do
    Process;
  DoSomethingWithI(I); // Compile error!!!
end;

上面的代码的好处在于,变量的范围仅限于声明它们的块。这样可以最大程度地减少出错的机会。例如,假设您有如下代码:

procedure Test;
var I: Integer;
begin
  I := CalculateSomething;
  Persist(I);
  // many lines below...
  Log(I);
end;

然后,您最终需要以第一部分仅在指定条件下执行的方式重构代码。您认为该变量I仅在此处使用,并执行以下操作:

procedure Test;
var I: Integer;
begin
  if Condition then
  begin
    I := CalculateSomething;
    Persist(I);
  end;
  // many lines below...
  Log(I);
end;

在那里,您忘记了最后一行,也许I的值不是您所期望的。如果在块外使用变量,则将变量的范围更改为块会产生编译错误,这将立即向您显示问题,因此您可以做出决定:

procedure Test;
begin
  if Condition then
  begin
    var I: Integer;
    I := CalculateSomething;
    Persist(I);
  end;
  // many lines below...
  Log(I); // Compile error!
end;

3.打字少

谁不希望提高生产力?如果可以少键入一些内容来声明变量,为什么不呢?现在,您可以同时声明和初始化变量:

procedure Test;
begin
  var I: Integer := 22; 
  ShowMessage (I.ToString);
end;

但不仅如此。还有一个类型推断,这意味着在大多数情况下,在声明它时不需要包括变量类型。只需使用值初始化变量,Delphi就会知道变量类型。

看起来没什么大不了的?想象一下一个变量类型使用大量泛型的情况:

procedure NewTest;
var
  MyDictionary: TObjectDictionary<string, TObjectList<TMyAmazingClass>>;
  Pair: TPair<string, TObjectList<TMyAmazingClass>>;
  List: TObjectList<TMyAmazingClass>;
begin
  MyDictionary := TObjectDictionary<string, TObjectList<TMyAmazingClass>>.Create;
  MyDictionary.Add('one', CreateList);
  Pair := MyDictionary.ExtractPair('one');
  List := Pair.Value;
  ShowMessage(List.Count.ToString);
end;

使用内联变量和类型推断,您可以通过以下方式重写代码:

procedure NewTest;
begin
  var MyDictionary := TObjectDictionary<string, TObjectList<TMyAmazingClass>>.Create;
  MyDictionary.Add('one', CreateList);
  var Pair := MyDictionary.ExtractPair('one');
  var List := Pair.Value;
  ShowMessage(List.Count.ToString);
end;

更好,不是吗?

4.提高性能

变量属于更有限范围的事实(在begin..end块内)甚至可以提高代码性能!

您可以在这篇出色的文章中看到更多详细信息:内联变量可以提高性能。概括来说:仅当代码执行进入块时,变量才会初始化;仅在块退出时,变量才会最终确定。在此代码中,例如:

procedure TestInlineVars(const ACondition: Boolean);
begin
  // BEFORE
  if (ACondition) then
  begin
    var S := 'Inline String';
    var I: IInterface := TInterfacedObject.Create;
    var F: TFoo;
    F.S := 'Managed Record';
  end;
  // AFTER
end;

变量S,I和F是托管类型(字符串,接口和记录)。编译器会自动为其添加初始化和完成代码。

如果您调用TestInlineVars过程一百万次,将会产生很大的影响。但是,使用上面的代码,只有在 ACondition为true并且实际执行该块的情况下,变量才会被初始化。减少不必要的代码被执行。

5.使使用条件指令更容易

此功能甚至可以在小事情上提供帮助。本文引起了我的注意:内联变量的意外好​​处:条件块

如果使用编译器伪指令在每种情况下声明并使用不同的变量,则还需要在编译器伪指令周围包装变量声明:

procedure DoesSomething;
var
  {$IFDEF CASE1}
  var1: Integer;
  {$ENDIF}
  {$IFDEF CASE2}
  var2: Integer;
  {$ENDIF
begin
  {$IFDEF CASE1}
  // use var1
  {$ENDIF}
  {$IFDEF CASE2}
  // use var2
  {$ENDIF}
end;

无聊吧?我认为这更容易:

procedure DoesSomething;
begin
  {$IFDEF CASE1}
  var1: Integer;
  // use var1
  {$ENDIF}
  {$IFDEF CASE2}
  var2: Integer;
  // use var2
  {$ENDIF}
end;

我认为,内联变量在未在此处列出的特定情况下仍会带来其他细微的好处。如果您认为还有其他好处,请发表评论。如果您不同意并且认为内联变量对于Delphi不是好消息,请也发表您的评论。只是不要忘记一件事:如果您不喜欢它,那就不要使用它!

原文:https://landgraf.dev/en/5-reasons-to-use-inline-variables-in-delphi/

上一篇:RestTemplate的异常 Not enough variables available to expand


下一篇:php-在ajax readystate()中没有alert()的情况下循环不起作用