Присвоение полям в свойстве массива в delphi - PullRequest
5 голосов
/ 21 января 2020

У меня есть класс, который хранит общие c элементы в массиве. Эти элементы доступны через свойство массива по умолчанию:

TMyList<TData> = class
private
  items: array of TItem<TData>;
public
  function get(position: integer): TData;
  procedure edit(position: integer; data: TData);
  property Values[position: integer]: TData read get write edit; default;
end;

implementation
function TMyList<TData>.get(position: integer): TData;
begin
result:= items[position];
end;

procedure TMyList<TData>.edit(position: integer; data: TData);
var
  item: TItem<TData>;
begin
  items[position]:= item;
end;
end;

В этом случае все элементы, которые я храню, относятся к типу TTest, который также имеет свои собственные свойства:

TTest = record
  private
      FTest: string;
      procedure setFTest(const Value: string);
  public
    property Test: string read FTest write setFTest;
end;

implementation
procedure TColouredPoint.setFTest(const Value: String);
begin
  FTest:= Value;
end;
end;

I хочу иметь возможность изменить значение FTest для экземпляра TTest следующим образом:

var
  points: TMyList<TTest>;
...
points[index].Test:= 'test';

, но это ничего не делает. Нет сообщения об ошибке, но значение точек [index] .Test не изменяется.

Вместо этого я должен сделать это:

var
  points: TMyList<TTest>;
  temp: TTest;
...
temp:= points[index];
temp.Test:= 'test';
points[index]:= temp;

Почему не работает первая версия?

Ответы [ 2 ]

11 голосов
/ 21 января 2020

Почему первая версия не работает?

Рассмотрим этот код

points[index].Test := 'test';

Индексированное свойство преобразуется компилятором в вызов функции и т. Д. компилятор эффективно компилирует это:

points.get(index).Text := 'test';

Теперь points.get(index) возвращает копию значения TTest. Так как вы ничего не назначаете, компилятор вводит локальную переменную для хранения возвращаемого значения.

Таким образом, ваш код становится:

var
  tmp: TTest;
...
tmp := points.get(index);
tmp.Text := 'test';

Это последнее, что когда-либо делается с tmp, и поэтому изменения, которые вы вносите в tmp.Text, просто отбрасываются, оставляя базовый объект нетронутым.


Эта проблема довольно трудна для решения при работе с типами значений .

Коллекция generi c Delphi TList<T> обеспечивает прямой доступ к базовому массиву, что позволяет напрямую работать с сохраненными значениями.

Другой подход заключается в использовании ссылка, а не значение. Один простой способ добиться этого - использовать T, который является классом, а не записью, то есть ссылочным типом, а не типом значения.

3 голосов
/ 21 января 2020

Это потому что TTest это record. Получатель списка возвращает копию фактической записи, и это то, что вы меняете. Он должен работать, когда вы объявляете TTest как class, но тогда вы должны позаботиться о его создании и уничтожении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...