内联变量声明是 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;
更新(2020 年 5 月 18 日):Darian Miller在他的博客文章“新发现的 Delphi 中内联变量的隐藏好处”中提供了使用内联变量的更多好处。
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 块内)甚至可以提高代码性能!
您可以在这篇优秀文章中看到更多详细信息:内联变量可以提高性能。总结:变量只有在代码执行进入 block 时才会初始化,并且只有在 block exit 时才会结束。在此代码中,例如:
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 为真并且块被实际执行时,变量才会被初始化。正在执行的不必要的代码更少。
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 来说不是好消息,也请留下您的评论。只是不要忘记一件事:如果您不喜欢它,请不要使用它!
Copyright © 2014 DelphiW.com 开发 源码 文档 技巧 All Rights Reserved
晋ICP备14006235号-8 晋公网安备 14108102000087号
执行时间: 0.044689893722534 seconds