こんにちは。ヤマヤタケシです。
昨日の記事の続きです。
不変型について書こうとしていたらなんだか真夜中のラブレター状態に盛り上がってしまい、話題が広がりすぎました。
ここには簡単にクローンのコストの比較を書きます。
■クローンのコストの比較の表
コピーの種類 | 実装の手間 | 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の方法を選ばなきゃいけない時もあるのでしょう。
しょうがない。
■まとめ
そもそものきっかけは、書き換えられたくないインスタンスをコピーして渡したいのだけど、コピーの種類や手法が複数あってどうしよう?でした。
やっとどのコピーを使えば良いのかを状況に合わせて選べそうです。
そんじゃまた。