Я просто отлаживал проблему с функцией, которая возвращает строку, которая имеет меня взволнованный. Я всегда предполагал, что неявная переменная Результата для функций, которые возвращают строку, была бы пуста в начале вызова функции, но следующий (упрощенный) код привел к неожиданному результату:
function TMyObject.GenerateInfo: string;
procedure AppendInfo(const AppendStr: string);
begin
if(Result > '') then
Result := Result + #13;
Result := Result + AppendStr;
end;
begin
if(ACondition) then
AppendInfo('Some Text');
end;
Вызывание этой функции многократно привело к:
"Some Text"
в первый раз,
"Some Text"
"Some Text"
во второй раз,
"Some Text"
"Some Text"
"Some Text"
в третий раз, и т.д.
Для фиксации его, я должен был инициализировать Результат:
begin
Result := '';
if(ACondition) then
AppendInfo('Some Text');
end;
Действительно ли необходимо инициализировать результат строковой функции? Почему (технически)? То, почему компилятор не испускает предупреждение "Возвращаемое значение W1035 функции 'xxx', могло бы быть не определено" для строковых функций? Я должен пройти весь свой код, чтобы удостовериться, что значение установлено, поскольку это не надежно для ожидания пустой строки из функции, если результат явно не установлен?
Я протестировал это в новом тестовом приложении, и результатом является то же.
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
S: string;
begin
for i := 1 to 5 do
S := GenerateInfo;
ShowMessage(S); // 5 lines!
end;
Это не ошибка, а « функция »:
Для строки, динамический массив, метод указатель или вариант результата, эффекты такие же, как , если результат функции был объявлен как дополнительный параметр var после заявленные параметры. Другими словами, вызывающий передает дополнительный 32-битный указатель, указывающий на переменную в который должен вернуть результат функции.
Т.е. your
function TMyObject.GenerateInfo: string;
Действительно ли это:
procedure TMyObject.GenerateInfo(var Result: string);
Обратите внимание на префикс « var » (а не « out », как вы могли ожидать!).
Это ТАКОЕ не интуитивно понятно, поэтому приводит ко всевозможным проблемам в коде. Рассматриваемый код - всего лишь один пример результатов этой функции.
Просмотрите и проголосуйте за этот запрос .
Мы сталкивались с этим раньше, я думаю, может быть, еще в Delphi 6 или 7. Да, даже если компилятор не беспокоится о предупреждении вас, вам все же нужно инициализируйте свои строковые переменные Result именно по той причине, с которой вы столкнулись. Строковая переменная инициализируется - она не начинается как ссылка на мусор - но, похоже, не инициализируется re , когда вы этого ожидаете.
Что касается того, почему это происходит ... не уверен. Это ошибка, поэтому не обязательно указывать причину. Мы видели это только тогда, когда вызывали функцию несколько раз в цикле; если мы вызывали его вне цикла, он работал должным образом.Похоже, что вызывающий выделял место для переменной Result (и повторно использовал ее, когда он неоднократно вызывал одну и ту же функцию, что приводило к ошибке), а не функция , выделяющая свою собственную строку (и выделяя новый для каждого звонка).
Если вы использовали короткие строки, то вызывающий объект выделяет буфер - это давно устоявшееся поведение для типов с большими значениями. Но для AnsiString это не имеет смысла. Возможно, команда компиляторов просто забыла изменить семантику, когда они впервые реализовали длинные строки в Delphi 2.
Это не ошибка. По определению никакая переменная внутри функции не инициализируется, включая Результат.
Таким образом, ваш Результат - undefind при первом вызове, и может содержать что угодно. Как это реализовано в компиляторе, не имеет значения, и вы можете получить разные результаты в разных компиляторах.
Похоже, что ваша функция должна быть упрощена следующим образом:
function TMyObject.GenerateInfo: string;
begin
if(ACondition) then
Result := 'Some Text'
else
Result := '';
end;
Обычно вы не хотите использовать Result справа от присваивания в функции.
В любом случае, строго в иллюстративных целях, вы также можете сделать это, хотя и не рекомендуется:
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
S: string;
begin
for i := 1 to 5 do
begin
S := ''; // Clear before you call
S := GenerateInfo;
end;
ShowMessage(S); // 5 lines!
end;
Это похоже на ошибку в D2007. Я только что протестировал это в Delphi 2010 и получил ожидаемое поведение. (1 строка вместо 5.)
Если вы думаете, что автоматическое управление строками сделано для облегчения вашей жизни, вы правы лишь отчасти. Все это также сделано для того, чтобы логика строк была последовательной и не возникало побочных эффектов.
Во многих местах есть строки, переданные по ссылке, переданные по значению, но все эти строки ожидают VALID строк, в счетчике управления памятью которых есть допустимое значение, а не значение мусора. Поэтому для того, чтобы строки оставались действительными, единственное, что можно сказать наверняка, - это то, что они должны быть инициализированы при первом введении. Например, для любой строки локальной переменной это необходимо, поскольку именно здесь вводится строка. Любое другое использование строк, включая function (): string (которая фактически является процедурой (var Result: string), как правильно указал Александр), ожидает только действительных строк в стеке, а не инициализированных . И действительность здесь исходит из того факта, что конструкция (var Result: string) говорит, что «я жду действительной переменной, которая определенно была введена ранее».ОБНОВЛЕНИЕ: из-за этого фактическое содержимое Result является неожиданным, но из-за той же логики, если это единственный вызов этой функции, который имеет локальную переменную слева, пустота строки в этом случае гарантируется.