TStringList 没有传递值

TStringList is not passing value

所以我有一个程序,就是获取 dom 个节点的列表。

procedure TmainForm.getNodeListByClass(className:string; outputList:TStringList);
var
  foundNode:TDomTreeNode;
  foundNodesList:TStringlist;
begin
foundNodesList:=Tstringlist.Create;

foundNode:=nodeFindNodeByClassName(DomTree.RootNode,className);
if Assigned(foundNode) then
    getNodeList(foundNode,foundNodesList);

outputList:=foundNodesList;
freeandnil(foundNodesList);
end;

以及正在使用它的程序

procedure TmainForm.getByXpathBtnClick(Sender: TObject);
var
  temp:TStringlist;

begin
temp:=TStringlist.Create;

temp.Add('testval');

getNodeListByClass('table_input',temp);
memo1.Lines:=temp;

getNodeListByClass('left iteminfo',temp);
dbgForm.memo1.Lines:=temp;

getNodeListByClass('left',temp);
dbgForm.memo2.Lines:=temp;

freeandnil(temp);
end;

而且我真的不明白,为什么它不起作用,第一个过程的结果总是空的。 我发现,当第一个过程正在执行时,"foundNodesList" 有正确的列表,并且将它设置为 "outputList" 也在工作,但是一旦它返回到第二个过程(在 "temp" 列表)它只是空的。 所以它从 "test" 中清除旧数据('testval' 我在开头写的内容),但不添加第一个结果。

有人能给我指出正确的方向吗?

问题就在这里

outputList := foundNodesList;
FreeAndNil(foundNodesList);

作业是参考作业。我认为您希望将 foundNodesList 的内容转移到 outputList 中。但是发生的事情是你最终得到两个引用同一个实例的变量。

您的代码很容易修复。您不需要临时字符串列表,您可以简单地填充传递给方法的字符串列表。

procedure TmainForm.getNodeListByClass(className: string; outputList: TStringList);
var
  foundNode: TDomTreeNode;
begin
  outputList.Clear;
  foundNode := nodeFindNodeByClassName(DomTree.RootNode, className);
  if Assigned(foundNode) then
    getNodeList(foundNode, outputList);
end;

注意在其他函数写的时候

memo1.Lines := temp;

这有点不同。 TMemoLines 属性 有一个 属性 setter 复制 右侧,而不是采用一个参考。因此,您执行分配给 Lines 的代码是正确的。

你必须明白对象是Delphi中的引用类型,并且这些引用是按值传递的。所以你的程序

procedure TmainForm.getNodeListByClass(className:string; outputList:TStringList);
var
  foundNode:TDomTreeNode;
  foundNodesList:TStringlist;
begin
foundNodesList:=Tstringlist.Create;

foundNode:=nodeFindNodeByClassName(DomTree.RootNode,className);
if Assigned(foundNode) then
    getNodeList(foundNode,foundNodesList);

outputList:=foundNodesList;
freeandnil(foundNodesList);
end;

永远不会改变调用者的outputList。事实上,行

outputList:=foundNodesList;

仅设置 getNodeListByClass 过程的 自己的局部变量 outputList,它只是指向调用者字符串列表的指针的副本。因此,指针的这个副本被更改,但实际对象和调用者指向它的指针保持不变。

此外,即使情况并非如此,您的代码也会有错误,因为

freeandnil(foundNodesList);

销毁字符串列表对象 foundNodesList,这是 outputList 指向的同一对象。因此,如果调用者能够看到 "new" outputList(如果它是一个 var 参数),它只会看到一个悬空指针(内存损坏错误)。

你需要的是

procedure TmainForm.getNodeListByClass(const className: string; outputList: TStringList);
var
  foundNode: TDomTreeNode;
  foundNodesList: TStringlist;
begin
  foundNodesList := TStringList.Create;
  try
    foundNode := nodeFindNodeByClassName(DomTree.RootNode, className);
    if Assigned(foundNode) then
      getNodeList(foundNode, foundNodesList);  
    outputList.Assign(foundNodeList);
  finally
    foundNodeList.Free;
  end;
end;

假设您的功能按照我认为的那样进行。但这可以简化为

procedure TmainForm.getNodeListByClass(const className: string; outputList: TStringList);
var
  foundNode: TDomTreeNode;
begin
  outputList.Clear;
  foundNode := nodeFindNodeByClassName(DomTree.RootNode, className);
  if Assigned(foundNode) then
    getNodeList(foundNode, outputList);  
end;

(不知道你是想追加还是替换列表,你得相应调整代码。)

此外,请注意您始终必须保护您的对象,例如使用 try..finally 块。您的代码绝不能泄漏资源(例如内存),即使引发异常也不行!