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←変更されてしまう
投稿日時 - 2009-04-28 21:34:21
> 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();
}
}
投稿日時 - 2009-04-29 01:26:57
ご返答ありがとうございます。
試してみましたができました。ありがとうございます。
ところで、まだあまり理解しきれてない中、いろいろ試してみたのですが
other.cc2.ConvertAll(item => item.Clone())
を
other.cc2.ConvertAll(item => item)
で実行してもできた(変更されない)のですが、これはなぜなのでしょうか?
できたとしてもこのような記述は良くないのでしょうか?
投稿日時 - 2009-04-29 17:25:06
このQ&Aは役に立ちましたか?
0人が「このQ&Aが役に立った」と投票しています
回答(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
が正しく得られます。
投稿日時 - 2009-04-29 20:27:13
ご返答ありがとうございます。
なるほど、たしかにこれだと変化しますね。
確認してみたのですが、自分はこちらを使って試していました・・・
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;
}
だからここでクローン作成していたから変化しなかったんだと思います。
参考になりました。
投稿日時 - 2009-04-29 21:26:48
DeepCopyといいつつ,cc2がMemberwiseCloneでしかコピーされていません。
この結果,cc2がtc1とtc2で共有されてしまい,tc1.cc2[0].iとtc2.cc[0].iが同じ値になっています。
MemberwiseCloneを使わずに実装することをお勧めします。
投稿日時 - 2009-04-28 21:59:25
ご返答ありがとうございます。
>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();
}
}
投稿日時 - 2009-04-29 00:23:14