none
クラスを保持するリストをコピーしたい。 RRS feed

  • 質問

  • クラスを保持するリストをコピーしたいのですが
    クラスは参照型ですので

    public class AAA
    {
      public  String type_;
    }

    public class BBB
    {
      public BBB( BBB bbb )
      {
          AAAs_ = new List<AAA>( bbb.AAAs_ );
      }
     
      public List<AAA> AAAs_ = null;
    }

    などとしても
    実体がコピーされるわけではないので
    コピー先を変更すると、当然コピー元の内容も変わってしまいます。

    その為、少々手荒ではありますが

    public class AAA
    {
      // コピーコンストラクタ生成
      public AAA( AAA aaa )
      {
        type_ = aaa.type_;
      }
      public  String type_;
    }

    public class BBB
    {
      public BBB( BBB bbb )
      {
        AAAs_ = new List<AAA>();
       
        foreach( AAA aaa in bbb.AAAs_ )
        {
          AAAs_.Add( new AAA( aaa ) );
        }
      }
     
      public List<AAA> AAAs_ = null;
    }

    と言った荒業?で解決しております。

    こう言った場合のセオリーと申しますか
    素直な方法は存在するのでしょうか?
    知識不足で大変申し訳御座いませんがご相談に乗って頂けますでしょうか?

    2009年4月10日 17:58

回答

  • コピーコンストラクタよりもICloneableインターフェースを実装するのがセオリーです。
    そしてICloneable.Clone()メソッドはMemberwiseClone()メソッドが使えます。
    public class AAA: ICloneable{
    public string type_;

    public object Clone(){
    // 簡易コピーでいい
    return MemberwiseClone();
    }
    }

    public class BBB: ICloneable{
    public List<AAA> AAAs_ = null;

    public object Clone(){
    // まず一通りの簡易コピーをとる
    var result = (BBB)MemberwiseClone();

    // AAAs_についてはdeep copy
    if( AAAs_ != null ){
    result.AAAs_ = new List<AAA>();
    foreach( var aaa in AAAs_ )
    result.AAAs_.Add( (AAA)aaa.Clone() );
    }

    return result;
    }
    }
    ICloneableインターフェースはジェネリック導入前のものであり、戻り値がobjectとされていますので、呼び出し元が適切なクラスへキャストする必要があります。
    また、このインターフェースではどこまでdeep copyでどこから簡易コピーなのかがわかりづらいためインターフェス設計として失敗だったという意見をどこかで見かけました。(けど今探しても見つかりませんでした。)

    ちなみに「public List<AAA> AAAs_ = null;」ですがコンストラクタで必ず初期化しnullにならないよう設計してあれば、メンバ利用者がアクセスする際にnullチェックする必要がなくなります。例えば「public List<AAA> AAAs_ = new List<AAA>();」とか。フィールドでなくプロパティを公開すべきとかそういう声もありそうですが…。
    • 回答としてマーク zilch_1975 2009年4月11日 5:47
    2009年4月10日 21:39
  • 追加情報として、参考になりそうなページをご紹介しておきます。

    ICloneable と MemberwiseClone
    http://blogs.wankuma.com/jeanne/archive/2006/04/06/22272.aspx

    シャローコピー と ディープコピー
    http://blogs.wankuma.com/jeanne/archive/2006/04/07/22287.aspx

    IClonableとIDisposable
    http://smdn.invisiblefulmoon.net/ikimasshoy/vbdotnet/clonableanddisposable.html


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    • 回答としてマーク zilch_1975 2009年4月11日 5:47
    2009年4月11日 1:30
    モデレータ

すべての返信

  • コピーコンストラクタよりもICloneableインターフェースを実装するのがセオリーです。
    そしてICloneable.Clone()メソッドはMemberwiseClone()メソッドが使えます。
    public class AAA: ICloneable{
    public string type_;

    public object Clone(){
    // 簡易コピーでいい
    return MemberwiseClone();
    }
    }

    public class BBB: ICloneable{
    public List<AAA> AAAs_ = null;

    public object Clone(){
    // まず一通りの簡易コピーをとる
    var result = (BBB)MemberwiseClone();

    // AAAs_についてはdeep copy
    if( AAAs_ != null ){
    result.AAAs_ = new List<AAA>();
    foreach( var aaa in AAAs_ )
    result.AAAs_.Add( (AAA)aaa.Clone() );
    }

    return result;
    }
    }
    ICloneableインターフェースはジェネリック導入前のものであり、戻り値がobjectとされていますので、呼び出し元が適切なクラスへキャストする必要があります。
    また、このインターフェースではどこまでdeep copyでどこから簡易コピーなのかがわかりづらいためインターフェス設計として失敗だったという意見をどこかで見かけました。(けど今探しても見つかりませんでした。)

    ちなみに「public List<AAA> AAAs_ = null;」ですがコンストラクタで必ず初期化しnullにならないよう設計してあれば、メンバ利用者がアクセスする際にnullチェックする必要がなくなります。例えば「public List<AAA> AAAs_ = new List<AAA>();」とか。フィールドでなくプロパティを公開すべきとかそういう声もありそうですが…。
    • 回答としてマーク zilch_1975 2009年4月11日 5:47
    2009年4月10日 21:39
  • 追加情報として、参考になりそうなページをご紹介しておきます。

    ICloneable と MemberwiseClone
    http://blogs.wankuma.com/jeanne/archive/2006/04/06/22272.aspx

    シャローコピー と ディープコピー
    http://blogs.wankuma.com/jeanne/archive/2006/04/07/22287.aspx

    IClonableとIDisposable
    http://smdn.invisiblefulmoon.net/ikimasshoy/vbdotnet/clonableanddisposable.html


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    • 回答としてマーク zilch_1975 2009年4月11日 5:47
    2009年4月11日 1:30
    モデレータ
  • 佐祐理さん
     親切丁寧にソースまで有難う御座いました。
     大変良く理解できました。
     
     通常は
     public List<AAA> AAAs_ = new List<AAA>(); としています。(^^;
    2009年4月11日 5:50
  • trapemiyaさん
     情報提供有難う御座いました。
     ICloneable の存在は知っていたのですが
     MemberwiseCloneは全く知りませんでした。
     メソッド名も分からず、何をどう調べようかと思っていたので大変参考になりました。
     有難う御座いました。
    2009年4月11日 5:52