#string #delphi #constant-expression #open-array-parameters
#строка #delphi #constant-выражение #open-array-параметры
Вопрос:
Я хочу передать аргументы форматирования Args
в функцию Format. Я нашел несколько примеров этого, но я не могу выяснить, как назначить строковую константу в члене TVarRec. Следующий код завершается ошибкой при компиляции с E2089 Invalid typecast
.
procedure TForm1.Button1Click(Sender: TObject);
var Arguments: array of TVarRec;
begin
SetLength(Arguments, 2);
Arguments[0].VInteger := 111;
Arguments[1].VAnsiString := PAnsiString('Text'); // I want to set this member
ShowMessage(Format('Decimal: %d, String: %s', Arguments));
end;
Кто-нибудь может подсказать мне, как установить константу string (или AnsiString) для члена TVarRec? Я использую Delphi 2009.
Большое спасибо
Комментарии:
1. Актуальная статья для людей, которые никогда раньше не видели этот сложный аспект Delphi: rvelthuis.de/articles/articles-openarr.html
2. Я написал ответ и удалил его, и кто-то другой сделал то же самое. Это было СЛОЖНО. Хороший ТОндрей.
3. Просто для справки: вам не обязательно делать все это. Вы можете вызвать Format напрямую (и гораздо проще):
ShowMessage(Format('Decimal: %d, String: %s', [111, 'Text']));
. Это работает везде, гдеTVarArgs
требуется (обычно дляarray of const
) — вы можете создатьVarArgs
массив, просто передав его как[StringItem, IntegerItem, BooleanItem]
и так далее.4. Вы правы, однако я рассматривал возможность передачи
Args
какarray of string
с переменной длиной в качестве аргументовFormat
функции.5. Вы правы, но трудно сказать, заметил ли пользователь комментарий до принятого ответа; однако все же было бы неплохо перефразировать его обычным способом, используя собственную функцию вместо
Format
Ответ №1:
Кажется, это работает в XE:
var
Args: array[0..1] of TVarRec;
S: AnsiString;
U: UnicodeString;
begin
S := 'Hello';
U := 'world';
Args[0].VType := vtAnsiString;
Args[0].VAnsiString := Pointer(S);
Args[1].VType := vtUnicodeString;
Args[1].VUnicodeString := Pointer(U);
Writeln(Format('%s, %s!', Args));
end;
Комментарии:
1. 1 и принять. Это работает также в Delphi 2009. Большое спасибо 🙂
2. Приведение типов
Pointer(S)
также должно работать (таким образом, позволяя использовать пустые строки). Похоже, проблема связана просто со строковыми литералами.3. Я изменил код, чтобы показывать аргументы как AnsiString, так и UnicodeString.
4. Вау. Это было сложнее, чем я думал, что это будет! В другом предыдущем удаленном ответе использовалась SetLength для массива TVarRec, и, похоже, во время выполнения происходит сбой, хотя он нормально компилируется. Я был удивлен этим.
5. Это то, что я пробовал в первый раз, но из-за того, что я пропустил добавление
VType
, я получал нарушение доступа. Спасибо всем!
Ответ №2:
Просто мои два цента. Ответ ТОндрея правильный, но я просто хотел подчеркнуть некоторые моменты. И комментарии — не самое подходящее место для этого.
Пожалуйста, имейте в виду, что на AnsiString
должно быть указано все время, TVarRec
пока будет использоваться.
Например, если вы пишете функцию, задающую массив TVarRec
, вы должны убедиться, что вы сделали временную копию AnsiString
созданного в функции значения в переменную, которая будет оставаться до тех пор, пока не будет использоваться TVarRec
массив. В противном случае может возникнуть нарушение произвольного доступа (не каждый раз, а только когда MM переназначает ansistring
память).
Например, следующий код неверен:
type
TVarRec2 = array[0..1] of TVarRec;
procedure SetVarRec(a,b: integer; var Result: TVarRec2);
begin
Result[0].VType := vtAnsiString;
Result[0].VString := pointer(AnsiString(IntToStr(a)));
Result[1].VType := vtUnicodeString;
Result[1].VString := pointer(UnicodeString(IntToStr(b)));
end;
Потому что AnsiString
и UnicodeString
временные переменные будут освобождены по завершении процедуры и Results[].VString
по-прежнему будут указывать на освобожденную память…
Использование класса или записи с некоторой временной частной строкой может помочь:
type
TMyVar2 = record
private
tmpA: AnsiString;
tmpB: UnicodeString;
public
VarRec: TVarRec2;
procedure SetVarRec(a,b: integer);
end;
procedure TMyVar2.SetVarRec(a,b: integer);
begin
VarRec[0].VType := vtAnsiString;
tmpA := AnsiString(IntToStr(a));
VarRec[0].VString := pointer(tmpA);
VarRec[1].VType := vtUnicodeString;
tmpB := UnicodeString(IntToStr(b));
VarRec[1].VString := pointer(tmpB);
end;
Конечно, в вашей программе у вас может быть уже существующий класс. В этом случае вам лучше использовать метод и некоторые частные временные строки. Для того, чтобы метод был многопоточным (т. Е. повторно вводимым), вы должны предоставить временные строки в качестве параметров…
Я использую этот трюк, чтобы иметь действительный динамический массив, TVarData
содержащий некоторое AnsiString
содержимое в классе. Фактически, TVarData
и TVarRec
оба используют такой указатель без ссылок на строки, что может сбивать с толку.
Имейте в виду, что проблемы, связанные с pointer(S)
операторами like, могут быть трудно отследить.
Ответ №3:
вы должны использовать массив значений ansistring, потому что вы должны использовать point
temps: array of AnsiString;
value: array of TVarRec;
...
max_count := 10;
setlength(temps,max_count);
setlength(value,max_count);
for i := 0 to max_count-1 do begin
temps[i] := IntToStr(i);
value[i].VType := vtAnsiString;
value[i].VString := Pointer(temps[i]);
end;
AdoTable.insertrecord(value);