【问题标题】:TStringList is not passing valueTStringList 没有传递值
【发布时间】:2020-03-31 08:50:53
【问题描述】:

所以我有一个程序,就是获取 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'我一开始写的),但不添加第一个的结果。

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

【问题讨论】:

    标签: delphi procedure tstringlist


    【解决方案1】:

    问题来了

    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 属性具有 复制 右侧的属性设置器,而不是获取引用。因此,您执行分配给Lines 的代码是正确的。

    【讨论】:

    • 我测试过,效果很好!我认为既然值已经传递给outputList,我可以销毁foundNodesList,不知道,这只是一个参考。谢谢!
    • 可能值得指出的是,问题不仅在于释放:调用者无论如何都不会看到outputList 的新值,因为它不是var 参数。当然,+1! :)
    • @AndreasRejbrand 你是对的。你的答案更好。
    【解决方案2】:

    您必须了解对象是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 指向的对象相同。因此,如果调用者能够看到“新”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 块。即使引发异常,您的代码也不能泄漏资源(例如内存)!

    【讨论】:

    • 创建一个新的字符串列表实例没有意义,还不如使用提供的那个
    • @David:确实,我刚刚对此发表了新评论。出于教学原因,我仍然想保留中间步骤。
    • 你还需要清除outputList,以防找不到节点,例如
    • 知道了。该字符串列表只是我从中提取此过程的地方的剩余部分。但是为什么要使用 free 而不是 freeandnil 呢?
    • @A.Daty: FreeAndNil 也可以在这里使用,但没有必要。唯一的区别是FreeAndNil 也将指针设置为nil(以避免出现悬空指针),但在这种情况下,指针变量是foundNodeList,它是一个局部变量,仅在两行代码,所以很明显无论如何没人会看到它的价值。不过,在其他情况下,FreeAndNil 是一个很好的做法。当您处理寿命更长且在nil 和非nil 状态之间变化的变量时,您经常需要
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-02
    • 1970-01-01
    • 1970-01-01
    • 2020-01-09
    相关资源
    最近更新 更多