コンストラクターまたはインスタンス関数を使用したオブジェクトインスタンスの複製の長所と短所は何ですか?
例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);
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
_のデフォルトパラメータをテストしましたが、機能するので、追加しました。
個人的なスタイルに依存する正しい方法はないと思います。 (そしてマルコが指摘したように、もっと多くの方法があります。)
また、ストリームを使用して割り当てを実装する場合、どのフィールドを使用可能にする必要があるかを心配する場所は1つだけです。
私はclone style-が好きですが、Java(または他のGC言語)でのみです。Delphiで何度か使用しましたが、ほとんどはCreate
およびAssign
。これは、オブジェクトの破棄の責任者がはるかに明確であるためです。