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; ?
解决方法
这是帕斯卡自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.
A property,like a field,defines an attribute of an object. But while a field is merely a storage location whose contents can be examined and changed,a property associates specific actions with reading or modifying its data. Properties provide control over access to an object’s attributes,and they allow attributes to be computed.
The declaration of a property specifies a name and a type,and includes at least one access specifier.
查看如何声明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糖,允许组件编写者在读取或写入值时引入副作用,并允许程序员认为该属性是标准字段并享受舒适那副作用.