3

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;
4

5 に答える 5

9

レコードは値型です。ループは配列内の各レコードのfor inコピーを返しているため、コンパイラエラーは実際にはそれを変更しても無駄であることを示しています。

昔ながらのforループを使用する必要があります。

for i := low(dtcp) to high(dtcp) do
  dtcp[i].Value := 0;
于 2011-07-13T08:41:33.537 に答える
3

関数によって返されるレコードにはいくつかの問題があります。(イテレータは関数を使用します)。コンパイラエラーは、追加の変数を使用して解決できます。

    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;
于 2011-07-13T08:31:08.113 に答える
2

レコードは値型であり、参照型ではありません。あなたの場合Param、配列からのレコードではなく、配列からのレコードのコピーです。そのため、コンパイラーはそれを定数と見なし、レコードの一時コピーに値を割り当てることを防ぎます。

于 2011-07-13T08:39:43.237 に答える
2

レコード型は値型です。for in単純なケースでは、ループを使用していると仮定しますTList<TRecordType>。イテレータは関数であるため、リスト内のレコードのコピーを返します。そのコピーを変更しても、リスト内のレコードには影響しません。古いバージョンの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ループでも同じです。イテレータ自体は、関数で実装される可能性が最も高いプロパティを使用します。関数の結果を変更することはほとんど意味がなく、変更がイテレーターがその値を取得した場所に伝播されないことを考慮すると、コンパイラーはそれを停止するために非常にうまく機能しています!

于 2011-07-13T08:45:32.897 に答える
0

はい、レコードは値型であるため、コピーを受け取りますが、それは実際の問題ではありません。主な問題は、for-inループによって返される値が関数の結果である可能性が非常に高く、コンパイラーが関数の結果への書き込みを許可しないことです。同じことが、たとえばプロパティにも当てはまります。

関数の結果が参照型(型付きポインターやオブジェクトなど)の場合、そのプロパティに書き込むことはできますが、アイテム自体を変更することはできません。関数が値型の場合、その一部に書き込むこともできません。

もちろん、値を変数に割り当てて更新することもできますが、他の人が言ったように、元の値を変更することはありません。

つまり、for-inループは、配列、リスト、セット、コレクションなどからの読み取りにのみ使用し、それらへの書き込みには使用しないでください。

于 2011-07-13T20:20:35.860 に答える