A generic list of records that contains dynamic array

arrays, delphi, generics, record, tlist

Solution

This example can be simplified like so, removing all reference to generics:

{$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.

The output is:

42
666

The reason for this is that a dynamic array is a reference type. When you assign to a variable of dynamic array type, you are taking another reference and not making a copy.

You can resolve this by forcing a reference to be unique (that is have just a simple reference). There are a number of ways to achieve this. For instance, you can call `SetLength` on the array that you want to be unique.

{$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.

Output:

42
42

So, in your code you can write it like this:

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

You can use a variety of other ways to enforce uniqueness, including `Finalize`, assigning `nil`, `Copy`, etc.

This issue is covered in some detail in the documentation. Here are the pertinent excerpts:

If X and Y are variables of the same dynamic-array type, X := Y points X to the same array as Y. (There is no need to allocate memory for X before performing this operation.) Unlike strings and static arrays, copy-on-write is not employed for dynamic arrays, so they are not automatically copied before they are written to. For example, after this code executes:

 var
   A, B: array of Integer;
   begin
     SetLength(A, 1);
     A[0] := 1;
     B := A;
     B[0] := 2;
   end;

the value of A[0] is 2. (If A and B were static arrays, A[0] would still be 1.) Assigning to a dynamic-array index (for example, MyFlexibleArray[2] := 7) does not reallocate the array. Out-of-range indexes are not reported at compile time. In contrast, to make an independent copy of a dynamic array, you must use the global Copy function:

 var
   A, B: array of Integer;
 begin
   SetLength(A, 1);
   A[0] := 1;
   B := Copy(A);
   B[0] := 2; { B[0] <> A[0] }
 end;

Problem

I have a generic list of records. these records contains a dynamic array like following ``` Type TMyRec=record MyArr:Array of Integer; Name: string; Completed: Boolean; end; var MyList:TList<TMyRec>; MyRec:TMyRec; ``` then I create the list and set the array length like followings ``` 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); ``` then i change the data in `MyArr` and i also change `MyRec.Name` and add another item to the list ``` MyRec.MyArr[0]:=5; // just for demonstration MyRec.Name:='Record 2'; MyRec.Completed:=false; MyList.Add(MyRec); ``` when `MyRec.MyArr` changes after adding the first item to the list, `MyArr` which is stored to the list also changes. however the other record fields does not. My question is how to prevent the changes in `MyRec.MyArr` to be reflected on the array which is already stored in the list item. do i need to declare multiple records.

Original source