"复印"不会创建动态数组的独立副本

时间:2014-05-03 15:31:22

标签: arrays delphi

参考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的一些功能。

3 个答案:

答案 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;

显然,上面的代码假设您可以使用无参数构造函数。

另请参阅:Correct way to duplicate Delphi object

答案 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)也可能是替代解决方案。