第一个程序:
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;
当我点击按钮时,备忘录没有显示任何内容,并且断点显示该过程不执行此行:
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;
?
答案 0 :(得分:15)
当您按值传递变量时,Delphi会在堆栈中复制参数值,并且该方法内对该参数所做的所有更改都将通过该副本完成。
这是帕斯卡自Turbo 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 property。
属性(如字段)定义对象的属性。但是,虽然字段仅是可以检查和更改其内容的存储位置,但属性会将特定操作与读取或修改其数据相关联。属性提供对对象属性的访问的控制,并且它们允许计算属性。
属性声明指定名称和类型,并包括至少一个访问说明符。
查看如何声明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处理对象属性并保持对对象的明确所有权的方法。每个组件都创建并拥有它自己的子对象,并创建和拥有对象。
属性上的赋值运算符(:=)是sintactic糖,允许组件编写者在读取或写入值时引入副作用,并允许程序员认为该属性是标准字段和享受那种副作用的舒适感。