在询问有关记录中的接口字段的这个问题后,我假设以下内容可以正常工作(注意断言):
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;
Run Code Online (Sandbox Code Playgroud)
我用以下代码测试了这个:
for I := 1 to 1000 do
Rec := Test.ReturnRec;
Run Code Online (Sandbox Code Playgroud)
并且断言失败了!
我的错在哪里?什么假设是错的?
gab*_*abr 12
功能
function ReturnRec: TRec<Integer>;
Run Code Online (Sandbox Code Playgroud)
在语义上等于过程
procedure ReturnRec(var Result: TRec<Integer>);
Run Code Online (Sandbox Code Playgroud)
[我很确定有人来自Embarcadero,可能是Barry Kelly或Alan Bauer在某个地方说过这个,但我现在找不到参考资料.]
在第二种情况下,编译器假定记录将在传递给ReturnRec之前初始化(如果需要),并且不会在ReturnRec内部为rec创建任何初始化代码.我假设在第一个示例中采用了编译器内部相同的代码路径,这就是为什么Result没有初始化.
无论如何,解决方案很简单:
function TTestClass.ReturnRec : TRec <Integer>;
begin
Result.Intf := TInterfacedObject.Create;
end;
Run Code Online (Sandbox Code Playgroud)
假设编译器知道它在做什么并分配接口,一切都会正常工作.
编辑
您遇到的问题来自'for'循环.你的代码
for I := 1 to 1000 do
Rec := Test.ReturnRec;
Run Code Online (Sandbox Code Playgroud)
被编译成这样的东西:
var
result: TRec<Integer>;
Initialize(result);
for I := 1 to 1000 do begin
Test.ReturnRec(result);
rec := result;
end;
Run Code Online (Sandbox Code Playgroud)
这就是为什么你重复使用相同的记录,这就是为什么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);
Run Code Online (Sandbox Code Playgroud)
现在隐藏的结果变量存在于GetRec过程中,并在每次调用GetRec时初始化.
| 归档时间: |
|
| 查看次数: |
1164 次 |
| 最近记录: |