在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/

 

 

posted @ 2020-01-06 19:49  塞翁失身  阅读(1430)  评论(1编辑  收藏  举报