C#でのオブジェクトのコピーについて

このQ&Aのポイント
  • C#でのオブジェクトのコピー方法を調査しています。参照渡しではなく値渡しでオブジェクトをコピーしたいと思っています。コピーする際にリストを使用する場合にはどのように記述すれば良いのか教えてください。
  • C#でオブジェクトのコピーについて質問です。リストを使用しない場合は、深いコピーを行うことができましたが、リストを使用する場合には一部のプロパティの値まで変更されてしまいます。リストを使用する際にも正しくオブジェクトをコピーする方法を教えてください。
  • C#でオブジェクトのコピーを行う際、参照渡しではなく値渡しにしたいと考えています。リストを使用する場合に値までコピーする方法がわかりません。リストを使用する場合にも正しくオブジェクトをコピーする方法を教えていただきたいです。
回答を見る
  • ベストアンサー

C#でのオブジェクトのコピーについて

visual studio 2008 express edition C#3.5 を使用しています。 参照渡しではなく値渡し(コピー元が影響されない)でオブジェクトをコピーしたいと思い、 次のようなかんじで深いコピーを行ったのですが、 tc1.cc1.i(リストを使用しない方)変更されなかったのですが tc1.cc2[0].i(リストを使用した方)は変更されてしまいます・・・ どこが原因で、リストを使用する場合はどのように記述すれば良いのでしょうか? public class TestClass { public int n; public ChildClass1 cc1 = new ChildClass1(); public List<ChildClass2> cc2 = new List<ChildClass2>(); public object DeepCopy() { TestClass tc = (TestClass)Clone(); tc.cc1 = (ChildClass1)cc1.Clone(); for (int i = 0; i < cc2.Count; i++) { tc.cc2[i] = (ChildClass2)cc2[i].Clone(); } return tc; } private object Clone() { return MemberwiseClone(); } } public class ChildClass1 : ICloneable { public int i; public object Clone() { return MemberwiseClone(); } } public class ChildClass2 : ICloneable { public int i; public object Clone() { return MemberwiseClone(); } } TestClass tc1 = new TestClass(); tc1.cc1.i = 1; ChildClass2 cc2 = new ChildClass2(); cc2.i = 1; tc1.cc2.Add(cc2); TestClass tc2 = (TestClass)tc1.DeepCopy(); tc2.cc1.i = 2; tc2.cc2[0].i = 2; System.Windows.Forms.MessageBox.Show("cc1.i = " + tc1.cc1.i + " cc2[0].i = " + tc1.cc2[0].i);// cc1.i = 1 cc2[0].i = 2←変更されてしまう

質問者が選んだベストアンサー

  • ベストアンサー
回答No.2

> tc.cc1 = cc1; > tc.cc2 = cc2; これでは浅いコピーになってしまいます。 とりあえず,私だったらこうする程度の物を。 # U+0009/U+0020が無視されるので,U+3000に置き換えています。 public class TestClass {  public int n;  public ChildClass1 cc1;  public List<ChildClass2> cc2;  public TestClass ()  {   n = 0;   cc1 = new ChidlClass1();   cc2 = new List<ChildClass2>();  }  protected TestClass (TestClass other)  {   n = other.n;   cc1 = other.cc1.Clone(); // cc1の深いコピー   cc2 = new List<ChildClass2>(other.cc2.ConvertAll(item => item.Clone())); // cc2の深いコピー : List<T>の要素単位でCloneする  }  public TestClass DeepCopy ()  {   return TestClass(this);   tc.cc1 = (ChildClass1)cc1.Clone();   for (int i = 0; i < cc2.Count; i++)   {    tc.cc2[i] = (ChildClass2)cc2[i].Clone();   }   return tc;  } } public class ChildClass1 : ICloneable {  public int i;  public ChildClass1 ()   : this(0)  {  }  public ChildClass1 (int value)  {   i = value;  }  protected ChildClass1 (ChildClass1 other)  {   i = other.i;  }  public ChildClass1 Clone ()  {   return new ChildClass1(this);  }  object ICloneable.Clone ()  {   return Clone();  } } public class ChildClass2 : ICloneable {  public int i;  public ChildClass2 ()   : this(0)  {  }  public ChildClass2 (int value)  {   i = value;  }  protected ChildClass2 (ChildClass1 other)  {   i = other.i;  }  public ChildClass2 Clone ()  {   return new ChildClass1(this);  }  object ICloneable.Clone ()  {   return Clone();  } }

takagoo100
質問者

お礼

ご返答ありがとうございます。 試してみましたができました。ありがとうございます。 ところで、まだあまり理解しきれてない中、いろいろ試してみたのですが other.cc2.ConvertAll(item => item.Clone()) を other.cc2.ConvertAll(item => item) で実行してもできた(変更されない)のですが、これはなぜなのでしょうか? できたとしてもこのような記述は良くないのでしょうか?

その他の回答 (2)

回答No.3

item => item.Clone() としないと,Listが同一のChildClass2への参照を保持したままになります。 四角と矢印を使って,現在どの変数がどのオブジェクトを参照しているかを図示しながら追ってみるとよいと思います。 お試し) using System; using System.Collections.Generic; class Program {  static void Main (string[] args)  {   TestClass tc1 = new TestClass();   tc1.cc2.Add(new ChildClass2(10));   Console.WriteLine(tc1.cc2[0].i);   TestClass tc2 = tc1.DeepCopy();   tc2.cc2[0].i = 15;   Console.WriteLine(tc1.cc2[0].i);  } } public class TestClass {  public int n;  public ChildClass1 cc1;  public List<ChildClass2> cc2;  public TestClass ()  {   n = 0;   cc1 = new ChildClass1();   cc2 = new List<ChildClass2>();  }  protected TestClass (TestClass other)  {   n = other.n;   cc1 = other.cc1.Clone(); // cc1の深いコピー   cc2 = new List<ChildClass2>(other.cc2.ConvertAll(item => item)); // cc2の浅いコピー : List<T>の要素をそのまま代入  }  public TestClass DeepCopy ()  {   return new TestClass(this);  } } public class ChildClass1 : ICloneable {  public int i;  public ChildClass1 ()   : this(0)  {  }  public ChildClass1 (int value)  {   i = value;  }  protected ChildClass1 (ChildClass1 other)  {   i = other.i;  }  public ChildClass1 Clone ()  {   return new ChildClass1(this);  }  object ICloneable.Clone ()  {   return Clone();  } } public class ChildClass2 : ICloneable {  public int i;  public ChildClass2 ()   : this(0)  {  }  public ChildClass2 (int value)  {   i = value;  }  protected ChildClass2 (ChildClass2 other)  {   i = other.i;  }  public ChildClass2 Clone ()  {   return new ChildClass2(this);  }  object ICloneable.Clone ()  {   return Clone();  } } 出力) 10 15 ちなみに,Cloneすると 10 10 が正しく得られます。

takagoo100
質問者

お礼

ご返答ありがとうございます。 なるほど、たしかにこれだと変化しますね。 確認してみたのですが、自分はこちらを使って試していました・・・ public TestClass DeepCopy() { TestClass tc = new TestClass(this); tc.cc1 = (ChildClass1)cc1.Clone(); for (int i = 0; i < cc2.Count; i++) { tc.cc2[i] = (ChildClass2)cc2[i].Clone(); } return tc; } だからここでクローン作成していたから変化しなかったんだと思います。 参考になりました。

回答No.1

DeepCopyといいつつ,cc2がMemberwiseCloneでしかコピーされていません。 この結果,cc2がtc1とtc2で共有されてしまい,tc1.cc2[0].iとtc2.cc[0].iが同じ値になっています。 MemberwiseCloneを使わずに実装することをお勧めします。

takagoo100
質問者

お礼

ご返答ありがとうございます。 >MemberwiseCloneを使わずに実装することをお勧めします。 一応、次のようなメンバを一つ一つコピーするやり方でやってみたのですが、やっぱり同じ結果でした・・・ 自分の理解ミスだと思いますが、こういうやり方ではないのでしょうか? public class TestClass { public int n; public ChildClass1 cc1 = new ChildClass1(); public List<ChildClass2> cc2 = new List<ChildClass2>(); public object DeepCopy() { TestClass tc = (TestClass)Clone(); tc.cc1 = (ChildClass1)cc1.Clone(); for (int i = 0; i < cc2.Count; i++) { tc.cc2[i] = (ChildClass2)cc2[i].Clone(); } return tc; } private object Clone() { TestClass tc = new TestClass(); tc.cc1 = cc1; tc.cc2 = cc2; return tc; //return MemberwiseClone(); } } public class ChildClass1 : ICloneable { public int i; public object Clone() { ChildClass1 cc1 = new ChildClass1(); cc1.i = i; return cc1; //return MemberwiseClone(); } } public class ChildClass2 : ICloneable { public int i; public object Clone() { ChildClass2 cc2 = new ChildClass2(); cc2.i = i; return cc2; //return MemberwiseClone(); } }

関連するQ&A

  • C# クラスのコピー

    VisualStudio2013 でC#を使って開発を行っています。 クラスのコピーについて質問させてください。     public class Config     {       public int H;       public int V;       public object Clone()       {         return MemberwiseClone();       }     }     public class Test     {       void Hoge()       {         Config org = new Config();         org.H = 10;         org.V = 20;                  Config copy;         copy = (Config)org.Clone();       }     }      このように記述した際にcopy とorgが別インスタンス?(アドレス?)を指していることは理解できています。 この場合、Clone()を読んだ際にnewされるイメージですが、 先にnewしたインスタンスに値だけを渡す方法はありますでしょうか?         Config copy = new Config();         // copy = org; // orgの中の値だけをcopyへ渡したい class Config に       public void Copy(Config cfg)       {         cfg.H = this.H;         cfg.V = this.V;       } と記述して、Clone()の代わりにすれば実現できるかなと思うのですが、実際は複雑なクラスの為うまいやり方がないかと思いました。 間違いやアドバイス等ありましたらなんでも良いのでお願いします。 よろしくお願いします。

  • オブジェクトのコピー

    以下のプログラムはVC++6.0(win2k上)で、 オブジェクトのコピーを練習するために書いたものです。 このプログラムは、コンパイルは通りますが、 実行エラーになります。どのように変更すればよいでしょうか? #include <iostream> #include <stdio.h> #include <assert.h> using namespace std; class Player{ int data; Player *p; public: Player(); ~Player(); Player(const Player & x); void SetData(int d){ data = d; } int GetData(){ return data; } //代入演算子 Player & operator=(const Player & x); }; Player & Player::operator = (const Player & x){ p = new Player; //Playerオブジェクトを別の場所に作る *p = *(x.p); //pの指すオブジェクトに、mのpが指すオブジェクトを代入 return *this; } Player::Player(const Player& x){ p = new Player; //Playerオブジェクトを別の場所に作る *p = *(x.p); //pの指すオブジェクトに、mのpが指すオブジェクトを代入 } class App { Player *p[3]; public: App(); ~App(){for(int i=0;i<3;i++)delete p[i];} //pの指すオブジェクトのデータを設定するだけ void SetData(int i1,int i2){ p[i1]->SetData(i2); } void init(){p[0]=new Player;p[1]=new Player;p[2]=new Player;} void copy(){p[0]=p[1];SetData(0,100);} void output(){ for(int i=0;i<3;i++)printf("%d\n",p[i]->GetData()); } }; void main() { App *x=new App; x->init(); for(int i=0;i<3;i++)x->SetData(i,i); x->copy(); x->output(); delete x; }

  • cloneメソッドはシャローコピー?

    質問させてください。 ネットを見ると、cloneメソッドはシャローコピーをするものだと認識できます。 しかし、以下のコードを実行すると、配列cnum[0]とnum[0]の値が異なる結果になり、ディープコピーを行っているように見えます。 public class Hairetu { public static void main(String[] args) { int []num={1,2,3}; int []cnum=new int[3]; int []dnum=new int[3]; cnum=num.clone(); cnum[0]=12; for(int i=0;i<3;i++) { System.out.println(cnum[i]+" "+num[i]); } } } cloneメソッドはディープコピーなのでしょうか。 どなたか教えてください。お願いします。

    • ベストアンサー
    • Java
  • C#のキャストについて(object→byte)

    いつもお世話になっております。C#初心者です。 「メソッドの引数として渡された値をリスト型の配列に格納する」という課題に取り組んでいますが、 変数のキャストで実行時にエラーが発生し、頭を悩ませています。 作成したコードは以下のようになっています。 public class TestClass {  private List<byte> hogeList = new List<byte>();  public void TestMethod( object hoge )  {   hogeList.Add( (byte)hoge ); ← (*)  } } 上記のメソッドをMain関数から TestClass test = new TestClass(); test.TestMethod(5); として実行すると、「hogeList」に「5」が格納される、といった動作にしたいと考えています。 しかしながら、ビルドは通るのですが、実行すると(*)の位置で止まり、 「指定されたキャストは有効ではありません。」 というメッセージが表示されます。 そこで質問なのですが、object型からbyte型にキャストする場合にはどのような記述の仕方がありますでしょうか? ご教授よろしくお願いいたします。

  • C# プロパティのスコープ/寿命について

    たびたびすみません。 いつもありがとうございます。 C#でウィンドウアプリケーションを作成しています。 今回下のような TestClass クラスを作りました。 TestClass クラスの中に Paths プロパティを持っているのですが、 このプロパティは、コンストラクタの一番下に到達した時点で開放されています。 プロパティのスコープってそういうものでしょうか。 この TestClass クラスは上位で new されてインスタンスが作成されていますが、 このインスタンスが存在する間はデータが保持されると思っていたのです。 逆に、インスタンスが存在する間、そのクラス内で値を保持するには、どうするのでしょうか? よろしくお願い致します。 public partial class MainForm : Form {   // プロパティ   TestClass Test {get; set;}   private void MainForm_Load(object sender, EventArgs e)   {     Test = new TestClass();   } } class TestClass() {   // プロパティ   string[] Paths {get; set;}   // コンストラクタ   // Dirクラスには、複数のディレクトリ情報が格納されている   public TestClass(Dir[] dirs)   {     try {       string[] TagPaths = new string[Const.MaxNum.tag];       // Dirクラスのディレクトリ情報のうち、パスを取り出す       for (int i = 0; i < n; i++) {         Paths[i] = dirs[i].Path;       }       // 某処理       ActiveFAPanel = SetupXXX(Paths);     }     catch(Exception ex)     {     }   } ←●●●● ここに来た時点でPathsはnullとなっている }

  • C++ オート変数にポインタを代入したら?

    処理の初めに、オブジェクトTestClassにデータm_intDataをセットします。その後、ループ内の関数Func内で、m_intDataを取得し、それを元にいろいろな処理をします。この時、Func内でのオート変数intDataTestにm_intDataのポインタを代入しているのですが、Funcを抜けた時にintDataTestがスコープから外れるために、intDataTest = m_intDataも開放されてしまうのでしょうか?もしそうなら、2度目にFuncを呼び出した時は、m_intDataは不定値になっていて、2度目のFuncを抜ける時、すでに開放した領域を開放しようとしてエラーになりますか? もっと複雑な本物コードを動かすと、1度目のFunc呼び出しでは有効値が入っていたintDataTestが、2度目では、すべて0になってしまっていました。 本当は、Funcが何回呼び出されても、最初に設定したintDataTestのデータを読み込みたいのですが、どのように直せばよいのかわからず、困っています。 また、私の理解が間違っていましたら、正しい解釈を教えて下さい。 //------------------------------------------------------------------------- class TestClass { private:   int* m_intData; public:   TestClass()   {     m_intData = NULL;   }   ~TestClass()   {     if( m_intData != NULL )     {       delete m_intData;       m_intData = NULL;     }   }   void SetData( int* intData )   {     m_intData = intData;   }   int* GetData()   {     return m_intData;   } }; //------------------------------------------------------------------------- void Func( TestClass* pClass ) {   int* intDataTest = pClass->GetData();   // この後、いろいろな処理 } //------------------------------------------------------------------------- int main() {   int* intData = NULL;   intData = new int[10];   for( int i = 0; i < 10; i++ )   {     intData[i] = i;   }   TestClass* pClass;   pClass->SetData( intData );   for( int i = 0; i < 5; i++ )   {     Func( pClass );     // この後、複雑ないろいろな処理   }   return  1; } //-------------------------------------------------------------------------

  • C#のListの要素がHashtableの並べ替え

    Listの要素がHashtableなのですが、Hashtableの特定のキーでListを並べ替える事ができるでしょうか? 例えば次の様な場合 int[] pid = new int[] { 2, 1, 3 }; string [] pname = new string[] { "田中", "鈴木", "本田" }; string[] pref = new string[] { "埼玉", "群馬", "千葉" }; int[] presult = new int[] { 65, 38, 52 }; List<object> usertList = new List<object>(); for (int i = 0; i <= 2; ++i) { Hashtable t_obj = new Hashtable(); t_obj["pid"] = pid[i]; t_obj["pname"] = name[i]; t_obj["pref"] = pref[i]; t_obj["presult"] = presult[i]; usertList.Add(t_obj); } usertListを pidに従って並べ替える presultに従って並べ替える presultとpidに従って並べ替える という事を行いたいのですが、どうすればよいか分かりません。 申し訳ありませんが、教えていただけないでしょうか?

  • Listをフィールドに持つクラスの複製が欲しいです。

    Listをフィールドに持つクラスの複製が欲しいです。 ただし、コピー元を変更しても、コピーしたものは変わってほしくないのです。 Serializableを実装することでディープコピーができるとのことで、サンプルに適当につくったBookクラスでテストしてみました。 public class Book implements Cloneable, Serializable { private static final long serialVersionUID = 8531245739641223373L; public String title; public String[] content; public List<String> list; public Book(){ title = "title"; content=new String[2]; list = new ArrayList<String>(); list.add("aaa"); } @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } } public static void main(String[] args) { Book orgBook = new Book(); orgBook.content[0] = "aaa"; orgBook.content[1] = "aaa"; // 二通りの複製方法 Book cloneBook = (Book)orgBook.clone(); Book copyBook = ObjectUtil.getCopy(orgBook); // オリジナルを変更 orgBook.content[1] = "bbb"; orgBook.list.clear(); orgBook.list.add("bbb"); // 複製した中身を確認 System.out.println("clone: " + cloneBook.content[1] + " " + cloneBook.list.get(0)); System.out.println("copy: " + copyBook.content[1] + " " + cloneBook.list.get(0)); } 出力結果は clone: bbb bbb copy: aaa bbb となりました。 上記のObjectUtil.getCopy(object)はシリアライズ・デシリアライズを通してコピーをつくるメソッドです。 cloneで作ったのはもちろん変更されるし、シリアライズで作ったコピーも配列はaaaのままですが、リストはbbbに変わってしまっています。 これがaaaのままになるようなコピーの作り方、ご存知の方教えて下さい。 対象となるクラスが複雑で大きいので、コピーメソッドをつくって一つずづコピーするというのはちょっと避けたいところです。 どうぞよろしくお願いします。 環境:Windows XPと7, jdk1.6, Eclipse3.5 から3.6

    • ベストアンサー
    • Java
  • Java オブジェクトのコピーについて

    Java オブジェクトのコピーについて 現在、Javaの勉強を行っているのですが、 オブジェクトのコピーについてどうしてもわからない部分があります。 以下についてご教授お願いいたします。 ----------------------------- (型定義) public class TypeAB{   List<String[]> ab = new ArrayList<String[]>;   public List<String[]> getAb(){     return ab;   }   public setAb(List<String[]> ab){     this.ab = ab;   } } ------------------------------ public class mst{   private void function(){     TypeAB hoge1 = new TypeAB();     TypeAB hoge2 = new TypeAB();     List<String[]> ab = new ArrayList<String[]>;     String[] a = {"1","2","3"};     String[] b = {"4","5","6"};     ab.add(a);     ab.add(b);     hoge1.setAb(ab);     hoge2.setAb(ab);   } } ------------------------------ 上記処理についてなのですが、 現在のままでは、最終的な[hoge][hoge1]には 変数[a][b]のメモリ上のアドレスが格納されているだけで、 hoge1.ab.get(0).a = {"4","5","6"}とした場合に、 hoge2の変数[a]の値まで変わってしまいます。 これをどうにかして最終的な、 hoge1.setAb(ab); hoge2.setAb(ab); の部分で別アドレスでコピーしたいのです。 言語:Java フレームワーク:SAStrutsです。 よろしくお願いいたします。

    • ベストアンサー
    • Java
  • Javaのプログラムの質問です。

    Javaのプログラムについての質問です。 Listインターフェースの実装クラスの自作と、作成したクラスの全メソッドを呼び出すサンプルを作成せよ、という問題です。  注意点として、java.util.Listの実装クラスは使用出来ません(ArrayListなど)。実装するメソッドは、コードの中に番号を振ってあります。 import java.util.Collection; import java.util.Iterator; import java.util.ListIterator; import java.util.List; class LocalList implements List{  private int Count;  private String Data[];  private Iterator ite;  private ListIterator lite;  // コンストラクタ  void mylist(){   Data = new String[10];   Count = 0;  }  (1)  public boolean add(Object str){   if(Count >= 10){    return false;   }   Data[Count ++] = new String((String)str);   return true;  }  public void add(int i,Object str){  }        public boolean addAll(Collection c){   return false;  }        public boolean addAll(int i,Collection c){   return false;  }    (2)  public void clear(){   Count = 0;  }  public boolean contains(Object str){   return false;  }          public boolean containsAll(Collection c){   return false;  }  public boolean equals(Object str){   return false;  }    (3)  public Object get(int i){   return (i >= Count);  }  public int hashCode(){   return -1;  }  public int indexOf(Object str){   return -1;  }  public boolean isEmpty(){   return false;  }  public Iterator iterator(){   return ite;  }     public int lastIndexOf(Object str){   return -1;  }     public ListIterator listIterator(){   return lite;  }     public ListIterator listIterator(int i){   return lite;  }    (4)  public Object remove(int i){   return (i >= Count);  }    public boolean remove(Object str){   return true;  }         public boolean removeAll(Collection c){   return false;  }         public boolean retainAll(Collection c){   return false;  }    (5)  public Object set(int i,Object str){   return Data[i];  }    (6)  public int size(){   return Count;  }  public List subList(int i,int j){   return this;  }  public Object[] toArray(){   return Data;  }  public Object[] toArray(Object[] a){   return Data;  } } class Main {  public static void main(String[] args) {   mylist sub = new mylist();   sub.add("ビルドバーニングガンダム");   sub.add("ライトニングガンダム");   sub.add("ウイニングガンダム");   sub.add("ガンダムフェニーチェリナーシタ");   sub.add("R・ギャギャ");   for(int i = 0; i < sub.size(); i++){      System.out.println(sub.get(i));   }   // 改行   System.out.println();   // setメソッド   sub.set(1,"ガンダムエピオン");   for(int i = 0; i < sub.size(); i++){    System.out.println(sub.get(i));   }   // 改行   System.out.println();   // sizeメソッド   System.out.println("\r\n" + "機体数は" + sub.size() + "です" + "\r\n");   // removeメソッド   sub.remove(1);   for(int i = 0; i < sub.size(); i++){       System.out.println(sub.get(i));   }   // clearメソッド   sub.clear();   System.out.println("\r\n" + "機体数が" + sub.size() + "になったので負けです");    } } setメソッドとremoveメソッド以外は起動するのようになったのですが、この2つがうんともすんとも動きません。ジェネリクス型を使うという考え方もあるらしいのですが、ネットで調べてもピンと来ず寸詰まり状態になってしまっています。後少しだと思うのですが。。。。 どなたかご教授頂けないでしょうか?よろしくお願い致します。

専門家に質問してみよう