先日、カスタム メソッドを使用してオブジェクトのディープ クローンを作成していましたが、さまざまな方法 (リフレクション、バイナリ シリアル化など) でディープ クローンを作成できることを知っているので、ちょっと疑問に思っていました:
Microsoft がディープ コピー メソッドをフレームワークに含めていない理由は何ですか?
この問題は、少なくとも一般的なケースでは、あなたが思っているよりもかなり難しいものです。
まず第一に、コピーは深いまたは浅いだけではなく、スペクトルです。
文字列の配列のリストがあり、そのコピーを作成したいと考えてみましょう。
最も浅いレベルから始めます。全体の参照を別の変数にコピーするだけです。いずれかの変数から参照されるリストへの変更は、もう一方に表示されます。
それでは、2 番目の変数に与える新しいリストを作成します。最初のリストの各項目を 2 番目のリストに追加します。これで、どちらかの変数から参照されるリストを、他の変数に見られずに変更できます。しかし、リストの最初の項目を取得して、最初の配列にある文字列を変更すると、両方のリストで表示されます!
ここで、新しいリストを作成します。最初のリストの各配列に対して新しい配列を作成し、基になる配列の各文字列を新しい配列に追加し、それらの新しい配列のそれぞれをに追加します新しいリスト。これで、変更を確認せずに、いずれかのリストの任意の配列を変更できます。しかし、待ってください、両方のリストはまだ同じ文字列を参照しています (これは結局のところ値型であり、内部的にデータの文字配列を持っています)。意地悪な人が来て、文字列の 1 つを変更した場合はどうなるでしょうか (安全でないコードを使用すると、実際にこれを行うことができます)。これで、すべての文字列をディープ コピーでコピーできます。しかし、それを行う必要がない場合はどうでしょうか。文字列を変更するほど意地悪な人がいないことを知ったらどうなるでしょうか? または、さらに言えば、両方のリストに反映されるはずです)。
もちろん、循環参照、実際にはその状態を表さないクラス内のフィールド(つまり、キャッシュされた値やクローンによって必要に応じて再計算される可能性のある派生データ) などの問題があります。
現実的には、すべての型を実装するIClonable
か、同等のものを用意し、それ自体を複製するための独自のカスタム コードを用意する必要があります。複雑なオブジェクトを複製する方法が非常に多いため、これは言語を維持するのに大変な作業になります。コストは非常に高く、利点 (クローン メソッドを実装する価値があると見なされる少数のオブジェクトを除く) は、一般的に価値がありません。あなたはプログラマーとして、必要な深さに基づいて、型を複製するための独自のロジックを記述します。
これは、C および C++ での動作 (または動作しない) と似ています。
ディープ コピーを行うには、さまざまなデータがどのように解釈されるかを実際に知る必要があります。些細なケースでは、(提供されている) 浅いコピーは深いコピーと同じです。しかし、これが真実ではなくなると、実際には実装と解釈に依存します。一般的な経験則はありません。
簡単な例としてゲームを使用してみましょう。
考えられる解決策は次の 2 つです。
UniqueID
コピーしてはならないことをコンパイラーに伝えることはできますが、同時に、これがどのように行われるかを定義することはできません。そして、できたとしても、ただ...CopyTo()
) を作成します。うーん..私の見解は次のとおりです。
A) 非常に深いコピーが必要になることはめったにない
ため B) フレームワークは、真に意味のあるオブジェクトの CLONE 方法を知ることを保証できない
ため C) 素朴な方法でディープ クローニングを実装するのは簡単で、1 つのメソッドと数行しかかからないためリフレクションと再帰を使用したコードの
しかし、それをカバーした古いMSDNの記事を見つけようとします
編集: 見つかりませんでした:(どこかで見たことは確かですが、今はグーグルで検索できません..ただし、関連するICloneableおよび派生物に関するいくつかの有用なリンク:
http://blogs.msdn.com/b/brada/archive/2004/05/03/125427.aspx http://blogs.msdn.com/b/mrtechnocal/archive/2009/10/19/why-not -icloneable-t.aspx https://stackoverflow.com/a/3712564/717732
著者の言葉を見つけられなかったので、ポイントを拡張させてください。
A: 非常にめったにコピーを深くしたくないからです。
おわかりのように、フレームワークはどのようにして一般的にどれくらい深くすべきかを推測できますか? 完全に深く、それが実装されているとしましょう。これで、memberwise-clone メソッドと total-clone メソッドができました。それでも、clone-me-but-the-root-base が必要になる場合もあります。したがって、彼らは別の質問を投稿します。なぜ完全クローンには生の塩基を切り離す方法がないのですか. または2番目に生。.Net チームの観点からは、ディープ クローンを提供してもほとんど何も解決されません。部分的なツールをいくつか見て怠け者であり、すべてを手に入れたいという理由だけで、私たちユーザーはそれについて怒鳴るでしょう:)
B)フレームワークは、オブジェクトを本当に意味のあるCLONEにする方法を知ることを保証できないため
特に、Entity Framework、.Net Remoting Proxies、COM-wrappers などのハンドルまたはネイティブのような ID を持ついくつかの特別なオブジェクトの場合: 上位クラスの階層レイヤーの読み取りと複製に成功するかもしれませんが、最終的には、その下のどこかで難解なものが見つかります。コピーしてはいけないことを知っているようなものIntPtr
です。その時間の大半。しかし、できる場合もあります。しかし、フレームワークのコードは普遍的でなければなりません。ディープ クローン作成は、特別に見えるクラス メンバーに対して多くの健全性チェックを行う非常に複雑なものでなければなりません。または、プログラマーが分析する必要のない基本クラスを持つものに対してディープ クローン作成を呼び出すと、危険な結果が生じる可能性があります。
B+) また、ツリー内の基底クラスが多いほど、パラメーター化されたコンストラクターが含まれる可能性が高くなり、直接コピーが適切でないことを示している可能性があることに注意してください。通常、直接コピー可能なクラスには、パラメーターなしのコンストラクターと、プロパティによってアクセス可能なすべてのコピー可能なデータがあります。
B++) フレームワークの設計者の観点から、メモリと速度を考慮すると、浅いコピーはほとんどの場合非常に高速ですが、深いコピーは正反対です。開発者が巨大なオブジェクトを自由にディープコピーできないようにすることは、フレームワークとプラットフォームの評判にとって有益です。とにかく、オブジェクトが軽量でシンプルな場合、ディープコピーが必要でしょうか? :) ディープ コピーを提供しないことで、開発者はディープ コピーの必要性について考えるようになります。これにより、通常、アプリケーションはより軽量で高速になります。
C) 単純な方法でディープ クローニングを実装するのは簡単で、リフレクションと再帰を使用して 1 つのメソッドと数行のコードを使用するためです。
浅いコピーがある場合、実際に深いコピーを作成するのはどれくらい難しいですか? それほど難しくありません!オブジェクト「obj」が与えられたメソッドを実装するだけです:
pseudocode:
object deepcopier(object obj)
newobject = obj.shallowcopy()
foreach(field in newobject.fields)
newobject.field = deepcopier(newobject.field)
return newobject
それだけです。もちろん、フィールドの列挙はリフレクションによって実行され、フィールドの読み取り/書き込みも実行する必要があります。
しかし、この方法は非常にナイーブです。これには重大な欠陥があります。あるオブジェクトに、同じ別のオブジェクトを指す 2 つのフィールドがある場合はどうなるでしょうか。それを検出し、一度クローンを作成してから、両方のフィールドをその 1 つのクローンに割り当てる必要があります。また、あるフィールドが指しているオブジェクトが、別のオブジェクトが指しているオブジェクトへの参照を持っている場合 (...) - これも、追跡して一度だけ複製する必要がある場合があります。また、サイクルはどうですか?ツリーの深いところにあるオブジェクトがルートへの参照を持っている場合は? 上記のようなアルゴは喜んで下降し、すべてをもう一度コピーし直して、最終的には StackOverflow で詰まるでしょう。
これにより、クローンの追跡が非常に難しくなり、シリアライゼーションのように見え始めます。実際、クラスが DataContract または Serializable である場合は、単純にシリアル化して逆シリアル化し、完全なディープ コピーを取得できます :)
オブジェクトが何を意味し、そのすべてのフィールドが何を意味するかを理解し、実際に複製する必要があるものと統合する必要があるものを認識しない限り、ディープ クローニングを普遍的な方法で行うことは困難です。開発者として、これが単なるデータ オブジェクトであり、完全に安全にディープ クローンを作成できることを知っている場合は、単にシリアライズ可能にする必要はありません。シリアライズ可能にできない場合は、おそらくディープクローンもできません!