C#…というか.NET Frameworkには ICloneable
というインタフェースが定義されています。
Clone
という自身の完全コピーなインスタンスを生成するメソッドを提供する、実装されていれば便利なインタフェースなのですが、一つ問題があります。
それは、クラスを継承する際の実装コストが高くなることです。
Clone
メソッドを持つクラスを継承することを考えればわかりますが、継承先のクラスでは Clone
メソッドを必ずオーバライドする必要があります(継承元で何らかの仕込みが行われている場合を除く)。
なぜなら Clone
メソッドはそのままではベースクラスのインスタンスを返してしまうからです。
なので ICloneable
インタフェースもしくはそれ相当の機能を実装する場合、 sealed
なクラスでない限りは派生先のことを考慮した実装にする必要があります。
最も無難ですぐに思い付くのは派生クラスから利用可能なコピーコンストラクタを用意することですが、他にも色々と方法があります。
.NET Framework系言語で利用可能な方法については次のサイトでいくつか挙げられています。
ただ、上のサイトで挙げられている方法は、コピーコンストラクタを利用するものを除くと.NET Framework系言語以外では利用可能な言語が限られます。
シリアライズはJavaや boost::serialization
を導入したC++でも使えますが、強引な手法だけに実行コストが高く付き、実用性は若干微妙です(クローン可能でない既存クラスのクローンを作成する方法としては便利かもしれませんが)。
まぁそんなわけで、結局のところコピーコンストラクタを使う方法が一番無難で、実質これしかない…と私は考えていました。
しかし、.NET Framework 3.0で導入された Freezable
クラス(System.Windows
名前空間)がよりスマートな方法で Clone
メソッドを実装していました。
Freezable
クラスでは Clone
メソッドは仮想メソッドではなく、派生クラスでオーバライドする必要もありません*1。
また、コピーコンストラクタも不要です*2。
その代わりに、 CreateInstanceCore
メソッドと CloneCore
メソッドをオーバライドする必要があります(前者は必須、後者は場合による)。
|
|
CreateInstanceCore
メソッドは CreateInstance
メソッドから呼び出され、派生クラスにおいてそのクラス型の新しいインスタンスを生成します。
このメソッドは Freezable
クラスから派生したクラスでは必ずオーバライドする必要があります。
とはいえ、 return new 派生クラス();
を書くだけなので実装コストは微々たるものです。
CloneCore
メソッドは、引数に渡された派生クラス型のインスタンスの内容を自身に詳細コピーします。
派生クラスでフィールドを追加した場合、 base.CloneCore(source);
を呼び出した上でそれらのフィールドもコピーするようにオーバライドする必要があります*3。
要するにコピーコンストラクタ相当の処理をこのメソッドで行うわけです。
この2つのメソッドを用いると、 Freezable
クラスの Clone
メソッドは次のように書くことができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 | - | ! - - | ! | - ! | | ! |
|
これで Clone
メソッドをオーバライドしなくとも派生先のクラスではそのクラス型のインスタンスを生成することができます。
.NET Framework固有の機能を用いているわけでもないので、クラスを扱える言語であれば大抵は実装可能です。
この方法がコピーコンストラクタを使う方法と比べて優れている点は次の通りです。
CloneCore
メソッドをオーバライドする必要が無い。
Clone
メソッドを必ずオーバライドする必要がある。逆にコピーコンストラクタを使う方法と比べて厄介になる点は、 readonly
なフィールドをコピーできないことでしょうか。
この方法を用いる場合、 readonly
なフィールドは実質使えません。
もっとも get
のみのプロパティを用いればいいのでさほど問題にはなりませんが(実装時にうっかり値を書き換えてしまうことを抑止できないくらい?)。
長々と書いた割にはそこまで圧倒的に優れている方法というわけではありませんが、まぁこんな方法もあるということで。