Это не нужно делать с интерфейсами. Это только с реализацией строки в Delphi.
Вы должны очистить переменную Result в качестве первой строки вашей функции.
Здесь ниже ваша функция фиксированной:
function tform1.nodeToSentence(nodeXml: string): string ;
var
tempXmlDoc : IXMLDocument;
resultWordPuncNodes : IXMLNodeList;
i: Integer;
begin
// Change #1 - added the line
Result := '';
// Variable Result is shared here before by both the function and the caller.
// You DO HAVE to clear the shared variable to make the function FORGET the previous result.
// You may do it by the 'function' or by the calling site, but it should have be done.
// Usually it is better to do it inside the function.
tempXmlDoc := CreateXMLDoc;
tempXmlDoc.LoadXML(nodeXml);
resultWordPuncNodes := XPathSelect(tempXmlDoc.DocumentElement,'//*');
for i:= 0 to resultWordPuncNodes.Length-1 do
begin
if resultWordPuncNodes.Item[i].NodeName = 'name' then
begin
if resultWordPuncNodes.Item[i].Attributes['attrib'] = 'v_attrib' then
begin
/// REMEMBER this line, it is important, I would explain later.
Result := TrimRight(Result);
Result := Result + resultWordPuncNodes.Item[i].Text + ' ';
end
else Result := Result + resultWordPuncNodes.Item[i].Text + ' ';
end;
end;
resultWordPuncNodes:=nil;
// Change #2 - removed the line - it is redundant
(* tempXmlDoc := nil; //interface based objects are freed this way *)
// Yes, they are. But Delphi automatically clears local ARC-variables
// when destroying them where the function exits.
// Variable should both be local and should be one of ARC types foe that.
end;
1) о авто-клиринговых местного и ARC-любезные переменных см http://docwiki.embarcadero.com/Libraries/XE8/en/System.Finalize
2) Ваша действительная функция не является функцией на всех.
// function tform1.nodeToSentence(nodeXml: string): string ; // only an illusion
procedure tform1.nodeToSentence(nodeXml: string; var Result: string); // real thing
Вы можете сказать, что писали функцию, а не процедуру. Однако это был просто синтаксический сахар. Это как TDateTime
и double
- это разные термины, но эти термины являются синонимами для одной и той же реализации;
Все функции, возвращающие переменные String/AnsiString/UnicodeString в Delphi, являются процедурами. Они просто замаскированы под функцию, а маскировка тонкая.
3) есть старый Delphi kōan об этом. Без OmniXML и всего сложного материала, который только размывает правду. Запустите этот код и посмотрите, как он себя ведет.
Function Impossible: String;
begin
ShowMessage('Empty string is equal to: ' + Result);
end;
Procedure ShowMe; Var spell: string;
begin
spell := Impossible();
spell := 'ABCDE';
spell := Impossible();
spell := '12345';
spell := Impossible();
end;
4) Теперь, вы могли бы это знать? Да, вы могли бы, это займет немного внимания. Давайте немного изменимся.
Function ImpossibleS: String;
begin
ShowMessage('Unknown string today is equal to: ' + Result);
end;
Function ImpossibleI: Integer;
begin
ShowMessage('Unknown integer today is equal to: ' + IntToStr(Result));
end;
Procedure ShowMe;
Var spell: string; chant: integer;
begin
spell := ImpossibleS();
spell := 'ABCDE';
spell := ImpossibleS();
spell := '12345';
spell := ImpossibleS();
chant := ImpossibleI();
chant := 100;
chant := ImpossibleI();
chant := 54321;
chant := ImpossibleI();
end;
Будьте внимательны и прочитайте предупреждения о компиляции. Вы исправили свой исходный код, который он компилирует, без каких-либо особых эмоций, не так ли?
Теперь скомпилируйте мой второй kōan. Прочтите предупреждения. Функция Integer генерирует предупреждение «Неинициализированная переменная». Строковая функция - нет. Это так ?
Почему разница? Поскольку строка является ARC-типом. Это означает, что строковая функция - это действительно процедура, а не функция. И это означает, что переменная Result инициализируется вызывающим абонентом вне видимой функции.Вы также можете заметить, что переменные chant
меняются после вызова, потому что целочисленная функция является реальной функцией, а назначение-после вызова является реальной. Наоборот, строковая функция является иллюзорной, а также иллюзорным назначением-после вызова. Он выглядит как назначенный, но это не так.
5) Последняя вещь. Почему я поместил эту метку в свой код.
/// REMEMBER this line, it is important, I would explain later.
Result := TrimRight(Result);
Точно из-за этого kōan выше. Вы, должно быть, читали мусор здесь. Вы, должно быть, использовали переменную «Результат», которую вы ранее не инициализировали. Вы, должно быть, ожидали, что ваш компилятор даст вам предупреждение в этой строке. Как это делает в ImpossibleI
настоящая функция выше. Но это не так. Точно так же, как с ImpossibleS
иллюзорной функцией. Это отсутствие предупреждения - это мертвая жертва иллюзии, которую создает здесь Delphi. Вы заметили бы, что ДОЛЖНО быть «неинициализированным var» предупреждением, но он пропал без вести, вы спросите себя, «кто инициализировал переменную, если это не моя функция», и вы поймете, что произошло с вами. ;-)
6) Хорошо, еще одна последняя вещь.
procedure StringVar(const S1: string; var S2: string);
begin
ShowMessage (S1 + S2);
end;
procedure StringOut(const S1: string; out S2: string);
begin
ShowMessage (S1 + S2);
end;
Эти две процедуры выглядят одинаково. Разница в S2. Это должен быть параметр OUT, а не параметр VAR (IN + OUT). Но Delphi терпит неудачу здесь только с вашей функцией. Delphi не может выполнять строковые OUT-параметры. Для сравнения, FreePascal (Lazarus, CodeTyphon) знает разницу и будет показывать предупреждение «неинициализированная переменная» для процедуры StringOut
.
Я добавил раздел призванию. – user30478
Да, вы забыли очистить переменную 'readableSentence', что, это не о XML –