この例は、ジェネリックへのすべての参照を削除して、次のように単純化できます。
{$APPTYPE CONSOLE}
var
x, y: array of Integer;
begin
SetLength(x, 1);
x[0] := 42;
y := x;
Writeln(x[0]);
y[0] := 666;
Writeln(x[0]);
end.
出力は次のとおりです。
42
666
これは、動的配列が参照型であるためです。動的配列型の変数に代入すると、別の参照が取得され、コピーは作成されません。
これを解決するには、参照を一意にする (つまり、単純な参照のみにする) ことを強制します。これを達成する方法はいくつかあります。たとえば、SetLength
一意にしたい配列を呼び出すことができます。
{$APPTYPE CONSOLE}
var
x, y: array of Integer;
begin
SetLength(x, 1);
x[0] := 42;
y := x;
SetLength(y, Length(y));
Writeln(x[0]);
y[0] := 666;
Writeln(x[0]);
end.
出力:
42
42
したがって、コードでは次のように記述できます。
MyList:=TList<TMyRec>.Create;
SetLength(MyRec.MyArr,5);
MyRec.MyArr[0]:=8; // just for demonstration
MyRec.Name:='Record 1';
MyRec.Completed:=true;
MyList.Add(MyRec);
SetLength(MyRec.MyArr,5); // <-- make the array unique
MyRec.MyArr[0]:=5; // just for demonstration
MyRec.Name:='Record 2';
MyRec.Completed:=false;
MyList.Add(MyRec);
Finalize
、割り当てnil
、Copy
などを含む、一意性を強制するための他のさまざまな方法を使用できます。
この問題については、ドキュメントで詳細に説明されています。関連する抜粋を次に示します。
X と Y が同じ動的配列型の変数である場合、X := Y は X を Y と同じ配列にポイントします (この操作を実行する前に X にメモリを割り当てる必要はありません)。文字列や静的配列とは異なり、 -on-write は動的配列には使用されないため、書き込まれる前に自動的にコピーされません。たとえば、次のコードが実行された後:
var
A, B: array of Integer;
begin
SetLength(A, 1);
A[0] := 1;
B := A;
B[0] := 2;
end;
A[0] の値は 2 です (A と B が静的配列の場合、A[0] は 1 のままです)。動的配列インデックス (たとえば、MyFlexibleArray[2] := 7) への代入は、配列を再割り当てします。範囲外のインデックスはコンパイル時に報告されません。対照的に、動的配列の独立したコピーを作成するには、グローバル Copy 関数を使用する必要があります。
var
A, B: array of Integer;
begin
SetLength(A, 1);
A[0] := 1;
B := Copy(A);
B[0] := 2; { B[0] <> A[0] }
end;