6

第一个程序:

procedure TestOne(List : TStringList);
var
  TempList : TStringList;
begin
  TempList := TStringList.Create;
  TempList.Add('Test');
  List := TempList;
  TempList.Free;
end;

procedure TForm1.Button1Click(Sender : TObject);
var
  aList : TStringList;
begin
  aList := TStringList.Create;
  TestOne(aList);
  Memo1.Lines := aList;
end;

当我点击按钮时,备忘录没有显示任何东西,断点显示程序没有执行这一行:</p>

  List := TempList;

这条线

我修改了程序:

procedure TestTwo(List : TStringList);
var
  TempList : TStringList;
begin
  TempList := TStringList.Create;
  TempList.Add('Test');
  List.Text := TempList.Text;
  //or
  List.Assign(TempList);
  //List := TempList;
  TempList.Free;
end;

这次它起作用了。

那为什么不能使用List := TempList;呢?

4

1 回答 1

15

当您按值传递变量时,Delphi 在堆栈中制作参数值的副本,并且在方法内对该参数所做的所有更改都在该副本上完成。

从 Turbo Pascal 时代开始,也许从一开始,这就是 pascal 的工作方式。考虑一下:

procedure TestInt(Int: Integer);
begin
  Int := 10;
  Writeln(Int); //writes 10
end;

var
  I: Integer;
begin
  I := 5;
  Wirteln(I); //writes 5
  TestInt(I);
  Writeln(I); //also writes 5
end.

现在,当您将对象作为参数传递时,您必须记住对象变量是对对象的引用(指向对象实际存储在堆中的地址的指针)。但是,如果您通过引用传递参数,上述规则仍然适用:在堆栈中制作引用的副本。您在方法/过程中对该引用所做的任何更改都是在该副本上完成的。

该行List := TempList;仅更改引用,使其指向不同内存位置中的不同对象。该值在过程返回时丢失,与整数值在TestInt过程返回时丢失的方式相同。

该行从不执行的事实是优化器在起作用。由于从未使用过新值,因此优化器会从最终的 exe 中消除分配,并且该行实际上从未执行过。

您可以更改参数声明以通过引用(var 参数)传递它,但这在处理对象时不是一个好主意,因为您必须考虑谁负责释放对象的内存以避免内存泄漏。

您没有告诉您要完成什么,但看起来分配是要走的路,因为分配将字符串从一个列表复制到另一个。您必须考虑直接在 List 上工作,而不是使用 TempList,如下所示:

procedure TestOne(List : TStringList);
begin
  List.Clear;
  List.Add('Test');
end;

procedure TForm1.Button1Click(Sender : TObject);
var
  aList : TStringList;
begin
  aList := TStringList.Create;
  TestOne(aList);
  Memo1.Lines := aList;
end;

如您所见,结果是相同的。

编辑

Rob 在评论中指出了一些重要的东西,所以我将解释为什么 Button1Click 方法的最后一行有效:Memo1.Lines := aList;

这看起来像是直接赋值,但您必须知道在那一行中您正在处理一个Delphi 属性

属性,就像字段一样,定义了对象的属性。但是,虽然字段只是一个可以检查和更改其内容的存储位置,但属性将特定操作与读取或修改其数据相关联。属性提供对对象属性访问的控制,并且它们允许计算属性。

属性的声明指定名称和类型,并包括至少一个访问说明符。

查看如何声明 TCustomMemo 的 Lines 属性:

  TCustomMemo = class(TCustomEdit)
  ..
  ..
    property Lines: TStrings read FLines write SetLines;

这意味着,当您为属性赋值时,您实际上是在调用 SetLines 方法,将 Value 作为参数传递,如下所示:

  Memo1.SetLines(AList);

SetLines 实现如下所示:

procedure TCustomMemo.SetLines(Value: TStrings);
begin
  FLines.Assign(Value);
end;

因此,您最终发出了相同的 TStrings.Assign 调用,它将所有字符串从源列表复制到目标列表。

这是 Delphi 处理对象属性并保持对对象的明确所有权的方法。每个组件都创建并拥有它自己的子对象,而您创建并拥有自己的对象。

属性上的赋值运算符 ( := ) 是语法糖,允许组件编写者在读取或写入值时引入副作用,并允许程序员将属性视为标准字段并享受舒适的副作用。

于 2013-01-23T03:16:36.943 回答