这个例子可以像这样简化,删除所有对泛型的引用:
{$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;