- 2023/5/31 内容を修正
C# でオブジェクトをディープコピーする DeepCopy.Expression というライブラリを作りました。
ディープコピーとは、オブジェクトの中に含まれるすべてのメンバや参照先のオブジェクトも新しく作成してコピーすることです。
ディープコピーするには、一般的にはシリアライズとデシリアライズを使う方法がありますが、これは速度や柔軟性に問題があります。
DeepCopy.Expression は、対象のオブジェクトの型ごとに式木を使って動的にコードを生成し、キャッシュすることで高速にコピーを行います。また [Cloneable] 属性を使ってコピーの挙動をカスタマイズすることもできます。
使い方
DeepCopy.Expression の使い方はとても簡単です。ObjectCloner クラスの Clone メソッドか CopyTo メソッドを呼び出すだけです。
Clone メソッドは、引数に渡したオブジェクトのディープコピーを作成して返します。CopyTo メソッドは、第一引数に渡したオブジェクトの内容を第二引数に渡したオブジェクトにコピーします。どちらのメソッドも、対象のオブジェクトが配列や匿名型であっても問題ありません。
例えば、以下のようなコードでディープコピーすることができます。
var target = new TestObject(); // コピーするオブジェクト
var cloned = ObjectCloner.Clone(target); // ディープコピーを作成
// 又は
TestObject destination;
ObjectCloner.CopyTo(target, destination); // ディープコピーを行う
パフォーマンス
DeepCopy.Expression のパフォーマンスは、他のライブラリと比べても優れています。以下は、同じオブジェクトを異なる回数だけディープコピーしたときの処理時間を比較したものです。
ライブラリ | 1回 | 100回 | 2,500回 | 10,000回 |
---|---|---|---|---|
A | 97 msec | 105 msec | 120 msec | 130 msec |
B | 28 msec | 32 msec | 41 msec | 50 msec |
C | 0 msec | 0 msec | 13 msec | 38 msec |
DeepCopy.Expression | 27 msec | 28 msec | 31 msec | 38 msec |
この結果からわかるように、DeepCopy.Expression は初回以降のディープコピーでは高速です。初回では式木の生成とコンパイルが行わるため、他のライブラリより遅くなりますが、その後はキャッシュされたコードで高速に処理されます。
コピーに使用したのは次のようなオブジェクトです。
using
class TestObject
{
int _num;
string _name;
DateTime _date;
int[] _array;
public static TestObject Create()
{
var random = new Random(Environment.TickCount);
var instance = new TestObject();
instance._num = random.Next();
instance._name = random.Next().ToString();
instance._date = DateTime.Now;
instance._array = new int[1024];
for (int i = 0; i < 1024; ++i)
instance._array[i] = random.Next();
return instance;
}
}
尚、 Ver 1.1.0 からは事前にキャッシュを生成しておく機能を実装しています。こうすることで、初回のコピー時から高速に動作させることが可能になっています。
まとめ
同じ型のオブジェクトを繰り返しディープコピーする必要があり、速度が求められるようなケースでは DeepCopy.Expression は有用ではないでしょうか。アプリの初期化時等で事前にキャッシュ生成しておけるならば、型毎に専用のディープコピーコードを書いた時と同等近い速度が得られます。
DeepCopy.Expression は、NuGetからダウンロードできます。詳細な使い方やソースコードは GitHub で確認できます。
2023年8月12日
素晴らしいです…
控えめに言ってもっと評価されるべきな気がします。
式木活用の好例ですね!
ライブラリCが気になるところです(^_^;)
2023年8月13日
コメントありがとうございます。
式木を使うことで初回のコンパイルにどうしても時間が掛かりますが、それが許容できるケースでは高速にコピー出来るようになりました。
現行版ではオブジェクトの構成によってはコピーできないものもあるため、その修正版を近いうちにリリース出来るように準備中です。