C#的なクローン手法のコスト比較

こんにちは。ヤマヤタケシです。
昨日の記事の続きです。

不変型について書こうとしていたらなんだか真夜中のラブレター状態に盛り上がってしまい、話題が広がりすぎました。
ここには簡単にクローンのコストの比較を書きます。

■クローンのコストの比較の表

コピーの種類 実装の手間 Cloneの実行コスト 操作コスト 予想外の変更
MemberwiseClone 浅いコピー 小さい 小さい 小さい 発生する
不変型連鎖 浅いコピー 小さい 小さい 大きい 発生しない
DataContractSerializer 深い 小さい 大きい 小さい 発生しない
独自の深いコピー 深い 大きい 大きい 小さい 発生しない

■解説
1. 何も考えずMemberwiseClone
System.Object.MemberwiseCloneは、メンバをコピーして新しいインスタンスを作ってくれる標準の手法です。
protectedなので、publicのCloneは自分で実装する必要があります。
メンバーにクラスがあるとコピー先とインスタンスを共有するので予想外の挙動につながります。

2. 不変型連鎖
intやstructなどの値型と、stringやSystem.Collection.Immutableなどの不変型だけをメンバーに持つクラスは、MemberwiseCloneをしても 1 の問題が発生しません。
その代わり、メンバーの値を変更するときはイチイチコピーが走るのがデメリットです。
素直に4をガンバルしかないかもしれません。
とはいえ、stringをメンバに持っていてもstringが不変型なので、この2の方法を使える事がわかって超うれしーです。

3. DataContractSerializer
シリアライズしてデシリアライズすることで深いコピーを実現します。
深いコピーを目的にやるにはちょっとオーバースペックな気がします。
とはいえ、実装は楽なのでパフォーマンスが重要ではないところでは選択したいですね。

4.独自の深いコピー
そもそも、これがメンドクセーので色々調べました。なんで、イチイチコピーを書かなきゃいけないの?メンバー変数を追加したら、コピーコードも追加するの?マジで?って感じでした。
結構、2の方法でなんとかなる場合が多そうなのでホッとしています。
それでも、複雑なクラスはこの4の方法を選ばなきゃいけない時もあるのでしょう。
しょうがない。

■まとめ
そもそものきっかけは、書き換えられたくないインスタンスをコピーして渡したいのだけど、コピーの種類や手法が複数あってどうしよう?でした。
やっとどのコピーを使えば良いのかを状況に合わせて選べそうです。

そんじゃまた。