【问题标题】:Function returning record with interface field带接口字段的函数返回记录
【发布时间】:2012-01-21 02:01:07
【问题描述】:

在询问this question about interface fields in records 之后,我认为以下方法会起作用(注意断言):

type
  TRec <T> = record
    Intf : IInterface;
  end;

  TTestClass = class
  public
    function ReturnRec : TRec <Integer>;
  end;

  // Implementation 
  function TTestClass.ReturnRec : TRec <Integer>;
  begin
    Assert (Result.Intf = nil);    // Interface field in record should be initialized!
    Result.Intf := TInterfacedObject.Create;
  end;

我使用以下代码对此进行了测试:

  for I := 1 to 1000 do
    Rec := Test.ReturnRec;

断言失败!

我的错误在哪里?什么假设是错误的?

【问题讨论】:

标签: delphi delphi-xe


【解决方案1】:

功能

function ReturnRec: TRec<Integer>;

语义上等于过程

procedure ReturnRec(var Result: TRec<Integer>);

[我很确定 Embarcadero 的某个人,可能是 Barry Kelly 或 Alan Bauer 在某处说过这一点,但我目前找不到参考资料。]

在第二种情况下,编译器假定记录将在传递给 ReturnRec 之前被初始化(如果需要),并且不会在 ReturnRec 内为 rec 创建任何初始化代码。我假设第一个示例采用编译器内部相同的代码路径,这就是未初始化 Result 的原因。

不管怎样,解决方法很简单:

function TTestClass.ReturnRec : TRec <Integer>;
begin
  Result.Intf := TInterfacedObject.Create;
end;

假设编译器知道它在做什么并分配接口,一切都会正常工作。

编辑

您遇到的问题来自“for”循环。你的代码

for I := 1 to 1000 do
  Rec := Test.ReturnRec;

被编译成这样的:

var
  result: TRec<Integer>;

Initialize(result);
for I := 1 to 1000 do begin
  Test.ReturnRec(result);
  rec := result;
end;

这就是为什么你要重复使用相同的记录,这就是为什么 Result.Intf 只是第一次未初始化。

EDIT2

您可以通过将 t.ReturnRec 调用从循环中移出到单独的方法中来欺骗编译器。

procedure GetRec(t: TTest; var rec: TRec);
begin
  rec := t.ReturnRec;
end;

for i := 1 to 1000 do
  GetRec(t, rec);

现在隐藏的结果变量存在于 GetRec 过程中,并在每次调用 GetRec 时被初始化。

【讨论】:

  • 谢谢你!我知道解决方法,但我不喜欢它,因为我将它用于实用程序列表记录(如 TList 但作为记录)并在所有地方调用 Initialize 不是很好(消除了部分拥有记录的优势):( 特别是因为忘记它会导致一些讨厌的错误。
  • @Smasher 您应该始终指定返回值。不要返回未初始化的值。
  • @Smasher 您当然可以将其视为一个错误。我敢打赌,你已经有很多 QC 报告了。无论如何,我的猜测是编译器永远不会改变,所以你只需要接受它。
  • @Smasher 我记错了。我关于for 循环的观点只有在你忽略返回值并且编译器必须创建一个隐式局部变量时才有效。
  • 调用Initialize正确的。它假定它接收到的值是invalid 并简单地用有效值覆盖记录。进入函数后,Result.Intf 中保存的值是有效的接口引用。 调用Initialize 会泄漏该引用。 如果需要,您可以先调用Finialize,但我认为将nil 分配给需要它的字段更直接。
猜你喜欢
  • 1970-01-01
  • 2019-03-20
  • 1970-01-01
  • 1970-01-01
  • 2019-02-14
  • 2019-09-27
  • 1970-01-01
  • 2016-08-09
  • 1970-01-01
相关资源
最近更新 更多