web-dev-qa-db-ja.com

Delphiオブジェクトを複製する正しい方法

コンストラクターまたはインスタンス関数を使用したオブジェクトインスタンスの複製の長所と短所は何ですか?

例A:

type
  TMyObject = class
  strict private
    FField: integer; 
  public
    constructor Create(srcObj: TMyObject); overload; 
    //alternatively:
    //constructor CreateFrom(srcObj: TMyObject);
    property Field: integer read FField;
  end;

constructor TMyObject.Create(srcObj: TMyObject);
begin
  inherited Create;
  FField := srcObj.Field;
end;

例B:

type
  TMyObject = class
  strict private
    FField: integer; 
  public
    function Clone: TMyObject;
    property Field: integer read FField;
  end;

function TMyObject.Clone: TMyObject;
begin
  Result := TMyObject.Create;
  Result.FField := FField;
end;

大きな違いの1つがすぐに思い浮かびます。後者の場合、TMyObjectに基づいてクローンをサポートするクラス階層を構築できるように、Createコンストラクターは仮想である必要があります。

これは問題ではないと仮定します。TMyObjectとそれに基づくすべてのものは完全に私の管理下にあります。 Delphiでコピーコンストラクタを実行するための好ましい方法は何ですか?どのバージョンが読みやすいと思いますか?前者または後者のアプローチをいつ使用しますか?議論する。 :)

編集:最初の例に関する私の主な懸念は、2番目のアプローチと比較して使用量が非常に多いことです。

newObj := TMyObject.Create(oldObj)

vs.

newObj := oldObj.Clone;

EDIT2または「なぜ単一行操作が必要なのか」

ほとんどの場合、Assignが合理的なアプローチであることに同意します。単にassignを使用して、「コピーコンストラクタ」を内部的に実装することも合理的です。

私は通常、オブジェクトをマルチスレッド化してメッセージキューに渡すときに、このようなコピーを作成しています。オブジェクトの作成が速い場合、オブジェクトの所有権の問題が本当に単純化されるため、通常は元のオブジェクトのコピーを渡します。

IOW、私は書くのが好きです

Send(TMyObject.Create(obj));

または

Send(obj.Clone);

newObj := TMyObject.Create;
newObj.Assign(obj);
Send(newObj);
21
gabr

1つ目は、作成するオブジェクトに関する情報を追加し、2つ目は作成しないオブジェクトを追加します。これは、インスタンス化するために使用できます。クラスの子孫または祖先

Delphiの方法(TPersistent)は、作成とクローン作成を分離します。

_dest := TSomeClass.Create; 
dest.Assign(source);  
_

また、インスタンス化するクラスを明示的に選択するのと同じプロパティがあります。ただし、2つのコンストラクターは必要ありません。1つは通常の使用用で、もう1つはクローンを作成する場所です。

1行の要件のために編集もちろんDelphiメタクラスを使用して混合できます(未テスト)

_type
  TBaseSomeObject = class;
  TBaseObjectClass = class of TBaseSomeObject;

  TBaseSomeObject = class(TPersistent)
    function Clone(t: TBaseObjectClass = nil): TBaseSomeObject; virtual;
  end;

...

  function TBaseSomeObject.Clone(t: TBaseObjectClass = nil): TBaseSomeObject;
  begin
    if Assigned(t) then
      Result := t.Create
    else
      Result := TBaseObjectClass(Self.ClassType).Create;
    Result.Assign(Self);
  end;


 SendObject(obj.Clone); // full clone.
 SendObject(obj.Clone(TDescandantObject)); // Cloned into Descendant object 
_

残りの部分については、assign()演算子を実装するだけで、複数の方法を組み合わせることができます。

edit2

上記のコードをD2009でテストされたコードに置き換えました。あなたを混乱させたかもしれないタイプのいくつかの依存関係があります、それがこの方法でより明確になることを願っています。もちろん、割り当てメカニズムを研究する必要があります。また、_metaclass=nil_のデフォルトパラメータをテストしましたが、機能するので、追加しました。

29

個人的なスタイルに依存する正しい方法はないと思います。 (そしてマルコが指摘したように、もっと多くの方法があります。)

  • コンストラクターの方法は短いですが、コンストラクターはオブジェクトのみを構築する必要があるという原則に違反しています。これはおそらく問題ではありません。
  • クラスごとに呼び出しを提供する必要がありますが、クローンの方法は短いです。
  • 割り当て方法は、Delphiに似ています。作成と初期化を分離します。これは、コードの保守を改善する1つのメソッドと1つの関数の概念が好きだからです。

また、ストリームを使用して割り当てを実装する場合、どのフィールドを使用可能にする必要があるかを心配する場所は1つだけです。

6
Toon Krijthe

私はclone style-が好きですが、Java(または他のGC言語)でのみです。Delphiで何度か使用しましたが、ほとんどはCreateおよびAssign。これは、オブジェクトの破棄の責任者がはるかに明確であるためです。

3
splash