参考http://docwiki.embarcadero.com/RADStudio/XE6/en/Structured_Types#Dynamic_Arrays上的在线文档。
很清楚地写道,要创建动态数组的独立副本,请使用Copy()函数。在该链接中找到的示例代码还说明了在复制之后,如果更改了一个数组,则它不会反映到另一个数组中,因为它们都是独立副本。但是,这对我不起作用。复制之后,如果我更改了一个数组,另一个数组会自动更改为接收相同的值,这意味着Copy()只执行X:= Y所做的操作。根据文档," X:= Y"和" X:=复制(Y)"是不相同。
这是我的代码:
type
TTestArray = array of TMyType; //Note: TMyType is a class and not a record.
var
X, Y, Z: TTestArray;
begin
SetLength(X, 1); //create first array
X[0].testString := 'Test Y';
Y := copy(X);
X[0].testString := 'Test Z'; //re-assign another value
Z := copy(X);
X[0].testString := 'Test X';
此时,testString字段应包含不同的文本。所以,
X[0].testString should be 'Test X'
Y[0].testString should be 'Test Y'
Z[0].testString should be 'Test Z'
但是,这三个人都只是测试X'因为testString中的值因此暗示Copy()没有创建数组X的独立副本。相反,所有三个数组都指向相同的内存位置。
任何可靠地创建动态数组的独立副本的方法(即完成我上面尝试做的事情)?
注意(稍后添加): TMyType是一个类而不是记录。因此,根据下面非常有用的评论,这是CLASS情况下的预期行为。那么,在这种情况下,如何将X独立复制到Y和Z?
注意2:已删除"错误"来自主题。对不起,Embarcadero ......
注3: TMyType必须是一个类。我无法控制它。它是从Web服务定义(WSDL)创建的,它公开了PeopleSoft的一些功能。
答案 0 :(得分:9)
问题在于您已将TMyType
定义为:
type
TMyType = class(SomeObject)
....
public
testString: string;
end;
当您创建初始TestArray
时,不会填充对象本身,但仅指向这些对象的指针。
发生了什么......
因此数组看起来像这样:
Array contents of array contents of the heap
-------------+----------------------+-----------------------
X[0] |-> pointer_to_MyType1 |-> MyType1
X[1] |-> pointer_to_MyType2 |-> MyType2
当您复制数组时,会发生以下情况:
Array contents of array contents of the heap
-------------+------------------------------+-----------------------
Y[0] |-> copy_of_pointer_to_MyType1 |-> MyType1
Y[1] |-> copy_of_pointer_to_MyType2 |-> MyType2
您希望看到什么......
这是你期望发生的事情:
Array contents of array
-------------+-------------------
X[0] or Y[0] |-> (copy of)MyType1
X[0] or Y[0] |-> (copy of)MyType2
如何实现这一目标
但是,为了实现这一目标,您必须将TMyType
定义为:
type
TMyType = record
public
TestString: string;
end;
现在记录本身将存储在数组中,而不是指向存储在别处的数据的指针。
在这种情况下如何将X独立复制到Y和Z?
如果您想继续使用课程,那么您必须编写自己的Copy
版本;类似的东西:
uses
System.SysUtils, system.classes, Generics.Collections;
type
TMyList<T: TPersistent, constructor > = class(TList<T>)
public
function CloneArray: TMyList<T>;
end;
implementation
function TMyList<T>.CloneArray: TMyList<T>;
var
i: integer;
temp: T;
begin
Result:= TMyList<T>.Create;
for i:= 0 to SizeOf(self) -1 do begin
temp:= T.Create;
temp.assign(self.items[i]);
Result.Add(temp);
end; {for i}
end;
显然,上面的代码假设您可以使用无参数构造函数。
答案 1 :(得分:3)
您应该创建一个新问题,而不是更改或添加到您的问题/
要回答问题的第二部分,最简单的方法是创建自己的CopyMyTypeArray函数。你的代码可能是这样的:
function CopyMyTypeArray(ASource: TTestArray): TTestArray;
var
newObject: TMyType;
begin
SetLength(Result, Length(ASource));
for cntr := Low(ASource) to High(ASource) do
begin
// You could put the lines below into a clone method
newObject := TMyType.Create;
newObject.testString := ASource[cntr].testString;
Result[cntr] := newObject;
end;
end;
请注意,如果释放动态数组,则需要释放对象。
如果您对此解决方案感到满意,请接受Johan的解决方案,因为这可以解答您的初始问题。
答案 2 :(得分:1)
如果需要独立的对象副本,最简单的方法是编写一个Copy Constructor。复制构造函数接受一个参数 - 对要复制的现有对象的引用 - 然后将其内部字段初始化为与原始对象中相同的值。这可以通过简单的代码或使用RTTI来完成。
另请参阅:Correct way to duplicate Delphi object
可配置的TMyTypeFactory(或TMyTypeBuilder)也可能是替代解决方案。