TStringList не передает значение - PullRequest
0 голосов
/ 31 марта 2020

Итак, у меня есть процедура, которая получает список узлов 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», что я пишу в начале), но не добавляя результат из первого.

Может ли кто-нибудь указать мне правильное направление?

Ответы [ 2 ]

2 голосов
/ 31 марта 2020

Вы должны понимать, что объекты являются ссылочными типами в 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 блоки. Ваш код никогда не должен пропускать ресурсы (например, память), даже если возникает исключение!

2 голосов
/ 31 марта 2020

Проблема здесь

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;

, это работает немного по другому. Свойство Lines TMemo имеет установщик свойств, который копирует с правой стороны, а не с использованием ссылки. Следовательно, ваш код, который выполняет присвоение Lines, является правильным.

...