如何初始化var Record参数

How to initialize var Record parameter

我有记录类型:

type
  TIPInfo = record
    IP,
    HostName,
    City,
    Region,
    Country,
    Loc,
    Org: WideString
  end;

函数到return记录数据:

function GetPublicIPInfo(var IPInfo: TIPInfo): Boolean;
begin
  // initialize
  FillChar(IPInfo, SizeOf(TIPInfo), 0);

  // populate data
  IPInfo.IP := GetVallue('ip');
  IPInfo.HostName := GetVallue('hostname');
  IPInfo.City := GetVallue('city');
  // etc...

  Result := IsOk;   
end;

来电者:

var
  IPInfo: TIPInfo;

if GetPublicIPInfo(IPInfo) then... // use data

通过调用 FillChar 来初始化 var TIPInfo 是正确的方法还是我应该将每个字段设置为空字符串?来电者应该这样做吗?

此外,在这种情况下使用 out 参数会更好吗(因为函数不读取数据)?

这里只使用 FillChar 是错误的。如果任何 WideString 成员不为空,那么您将以这种方式泄漏它们。相反,我建议如下:

Finalize(IPInfo);
FillChar(IPInfo, SizeOf(TIPInfo), 0);

或者另一种方法是将默认记录定义为类型常量:

const
  DefaultIPInfo: TIPInfo = ();

然后你可以使用简单赋值:

IPInfo := DefaultIPInfo;

在 Delphi 的现代版本中,您可以改用这段更具可读性的代码:

IPInfo := Default(TIPInfo);

有关此特定主题的更多信息,请参阅以下主题:

  • .
  • How to properly free records that contain various types in Delphi at once?

请注意,您的代码中的漏洞很难找到,因为 WideString 变量是作为 COM BSTR 对象实现的,并分配在 COM 堆上。因此,如果您对 Delphi 内存管理器使用内存泄漏检测,则不会检测到泄漏,因为它是从不同的堆泄漏的。

在你的例子中,因为你的记录是一个托管类型,并且只包含托管类型,那么你可以使用一个 out 参数来达到很好的效果。对于托管类型,out 参数意味着编译器将在调用站点生成代码,以在传入记录之前默认初始化记录。

考虑以下程序:

{$APPTYPE CONSOLE}

type
  TRec = record
    Value: WideString;
  end;

procedure Foo1(var rec: TRec);
begin
end;

procedure Foo2(out rec: TRec);
begin
end;

procedure Main;
var
  rec: TRec;
begin
  rec.Value := 'Foo';
  Foo1(rec);
  Writeln(rec.Value);
  Foo2(rec);
  Writeln(rec.Value);
end;

begin
  Main;
end.

输出为:

Foo

如果您的记录包含托管和非托管类型的混合,那么情况就不太好了。

{$APPTYPE CONSOLE}

type
  TRec = record
    Value1: WideString;
    Value2: Integer;
  end;

procedure Foo1(var rec: TRec);
begin
end;

procedure Foo2(out rec: TRec);
begin
end;

procedure Main;
var
  rec: TRec;
begin
  rec.Value1 := 'Foo';
  rec.Value2 := 42;
  Foo1(rec);
  Writeln(rec.Value1);
  Writeln(rec.Value2);
  Foo2(rec);
  Writeln(rec.Value1);
  Writeln(rec.Value2);
end;

begin
  Main;
end.

输出为:

Foo
42

42

只有托管成员默认初始化为 out 个参数。所以最好的办法是默认自己初始化变量,即使它作为 out 参数传递。

有关 out 参数的更多信息,请参见此处:What's the difference between "var" and "out" parameters?