Использование предложения «for in» и ошибки компилятора E2064 - PullRequest
3 голосов
/ 13 июля 2011

Я хочу использовать для предложения in в моем тестовом примере под D2010.

Если я хочу записать в переменную Param.Value , то компилятор сообщает об ошибке 2064, но позволяет записать в Param.Edit.text из той же записи, почему?

ДЕЛО ТЕСТА:

type
//
  TparamSet = (param_A, param_B, param_C, param_D, param_E, param_F);

  TParam = record
    Edit        :TEdit;
    Value       :integer;
  end;

var
  dtcp                  :array [TparamSet] of TParam;

procedure ResetParams;
var
  Param                 :TParam;
  A                     :Integer;
begin
  for Param in dtcp do
  begin
    Param.Edit.text:= 'Test';             //No problem
    A := Param.Value;                     //No problem
    Param.Value := 0;                     //Error: E2064 Left side cannot be assigned to;
  end;
end;

Ответы [ 5 ]

9 голосов
/ 13 июля 2011

Записи являются типами значений. Цикл for in возвращает копию каждой записи в массиве, поэтому ошибка компилятора фактически говорит вам, что модифицировать ее бесполезно.

Вам нужно будет использовать старомодный цикл for:

for i := low(dtcp) to high(dtcp) do
  dtcp[i].Value := 0;
3 голосов
/ 13 июля 2011

Есть некоторые проблемы с записями, возвращаемыми функцией.(Итератор использует функцию).Ошибка компилятора может быть решена с помощью дополнительной переменной:

    Param2 := Param;
    Param2.Value := 0;                     // This works (D2010)

Но это все равно не обновляет массив dtcp.Что не странно, потому что записи копируются, а копия обновляется.Вы можете решить эту проблему с помощью классов (или указателей на записи):

TParam = class
  Edit        :TEdit;
  Value       :integer;
end;

Теперь оригинальный код работает.Но не забывайте создавать и уничтожать классы.

procedure ResetParams;
var
  Param                 :TParam;
  A                     :Integer;
begin
  for Param in dtcp do
  begin
    Param.Edit.text:= 'Test';             //No problem
    A := Param.Value;                     //No problem
    Param.Value := 0;                     // This works (D2010)
  end;
end;
2 голосов
/ 13 июля 2011

Типы записей являются типами значений;Для простого случая, давайте предположим, что вы используете цикл for in над TList<TRecordType>.Поскольку итератор является функцией, он вернет копию записи в списке.Изменение этой copy не влияет на запись в списке;Даже если компилятор позволил вам сделать это, как это было в более старых версиях Delphi, результат не будет таким, как вы ожидаете.

Представьте себе функцию, возвращающую простой Integer (также тип значения).Вы никогда бы не подумали о написании такого кода, потому что интуиция говорит вам, что это не имеет смысла:

function aFunc(aParam:Integer): Integer;
begin
  Result := 7;
end;

procedure Test;
begin
  aFunc(8) := 9; // This doesn't compile and intuition tells you it's *very* wrong.
end;

... но ситуация с записью такая же.Представьте себе этот пример:

type TTestRecord = record
  aField: Integer;
end;

function TestFunc: TTestRecord;
begin
  Result.aField := 9;
end;

procedure TestProc;
begin
  TestFunc.aField := 10; // This also doesn't make any sense, but intuition might
                         // fail on this one. Older versions of the compiler allowed this!
end;

Ситуация такая же с циклом for in.Сам итератор использует свойство, которое, скорее всего, реализовано с помощью функции.Поскольку изменение результата функции не имеет большого смысла, и, учитывая, что изменение не будет распространено обратно туда, где значение получено итератором, компилятор очень хорошо останавливает его!

2 голосов
/ 13 июля 2011

Записи являются типами значений, а не ссылочными типами. В вашем случае Param - это не запись из массива, а копия записи из массива. Вот почему компилятор считает его константой и не позволяет присваивать значение временной копии записи.

0 голосов
/ 14 июля 2011

Да, записи являются типами значений, поэтому вы получаете копию, но это не является реальной проблемой. Основная проблема заключается в том, что значение, возвращаемое циклом for-in, скорее всего, будет результатом функции, а компилятор не позволяет записывать в результат функции. То же самое относится, например, к свойствам.

Если результатом функции является ссылочный тип, например, типизированный указатель или объект, вы можете записать его свойства, но вы не можете изменить сам элемент. Если функция является типом значения, вы также не можете # писать в ее части.

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

Это все означает, что циклы for-in должны использоваться только для чтения из таких вещей, как массивы, списки, наборы, коллекции и т. Д., А не для записи в них.

...