MemberwiseCloneを説明する
概要
C#に組み込み関数として用意されている「MemberwiseClone」関数を理解して、使えるようになりたい方への記事。
結論
MemberwiseCloneはオブジェクトをShallowCopyする。
解説
型を知ろう
C#には値型と参照型が定義されている。
値型は値そのものを、参照型は参照を保持している。
値型
値型は、データそのものを直接保持し、メモリ上の スタック(Stack) に格納されます。
代入やコピー時には 値そのものがコピー されるため、元の変数には影響を与えません。
代表的な値型
- 基本データ型:int, double, char, bool
- 構造体(struct):DateTime, Point など
- 列挙型(enum)
値型の動作
int a = 10;
int b = a; // 値がコピーされる
b = 20;
Console.WriteLine(a); // 10(元の値は変わらない)
Console.WriteLine(b); // 20
参照型
参照型は、データそのものではなく、データが格納されているメモリのアドレス(参照)を保持 します。
そのため、メモリ上の ヒープ(Heap) に格納されます。
代入やコピー時には 参照(アドレス)がコピー されるため、複製した変数を変更すると元の変数も影響を受けます。
代表的な参照型
- クラス(class):string, List, object など
- 配列(array):int[], string[]
- デリゲート(delegate)
参照型の動作
class Person
{
public string Name;
}
Person p1 = new Person { Name = "Alice" };
Person p2 = p1; // 参照(アドレス)がコピーされる
p2.Name = "Bob"; // p2 を変更
Console.WriteLine(p1.Name); // "Bob"(p1 も影響を受ける)
コピーには種類があることを知ろう
あるオブジェクトを複製(コピー)したい場合、その方法は2種類存在する。
1つ目は浅いコピー、2つ目は深いコピー。
この2つを使い分ける際に、参照型をフィールドに持つかが重要になってくる。
浅いコピー(Shallow Copy)とは?
「オブジェクトのトップレベルのフィールドのみをコピーする」 方法です。
値型のフィールドは値がコピーされますが、参照型のフィールドはアドレス(参照)だけがコピーされます。
つまり、コピー後のオブジェクトと元のオブジェクトは、参照型のフィールドを共有 することになります。
class Address
{
public string City;
}
class Person
{
public string Name; // 値型
public int Age; // 値型
public Address Address; // 参照型
public Person ShallowCopy()
{
return (Person)this.MemberwiseClone();
}
}
class Program
{
static void Main()
{
Person original = new Person { Name = "Alice", Age = 25, Address = new Address { City = "Tokyo" } };
Person copy = original.ShallowCopy(); // 浅いコピーを作成
copy.Name = "Bob"; // コピーの Name を変更(影響なし)
copy.Age = 30; // コピーの Age を変更(影響なし)
copy.Address.City = "Osaka"; // コピーの Address.City を変更(影響あり)
Console.WriteLine(original.Name); // "Alice"(影響なし)
Console.WriteLine(original.Age); // 25(影響なし)
Console.WriteLine(original.Address.City); // "Osaka"(影響を受けた!)
}
}
浅いコピーのポイント
✔ 値型フィールド(Age
)は独立してコピーされるため、変更しても影響しない。
❌ 参照型フィールド(Address
)は共有されるため、コピー先で変更すると元のオブジェクトも影響を受ける。
深いコピー(Deep Copy)とは?
「オブジェクトのすべてのフィールドを、新しいインスタンスとして再帰的にコピーする」 方法です。
参照型のフィールドも 新しいインスタンスを作成 するため、コピー後のオブジェクトは元のオブジェクトと完全に独立します。
class Address
{
public string City;
public Address DeepCopy()
{
return new Address { City = this.City }; // 新しい Address インスタンスを作成
}
}
class Person
{
public string Name;
public int Age;
public Address Address;
public Person DeepCopy()
{
return new Person
{
Name = this.Name,
Age = this.Age,
Address = this.Address.DeepCopy() // Address も新しく作成
};
}
}
class Program
{
static void Main()
{
Person original = new Person { Name = "Alice", Age = 25, Address = new Address { City = "Tokyo" } };
Person copy = original.DeepCopy(); // 深いコピーを作成
copy.Name = "Bob"; // 影響なし
copy.Age = 30; // 影響なし
copy.Address.City = "Osaka"; // 影響なし(独立している!)
Console.WriteLine(original.Name); // "Alice"(影響なし)
Console.WriteLine(original.Age); // 25(影響なし)
Console.WriteLine(original.Address.City); // "Tokyo"(影響なし!)
}
}
MemberwiseCloneと=代入演算子は異なる
あれ?これなら新しく変数を用意して代入すればよくない?と思った方。
それは間違えです。
代入は値型は値をそのまま、参照型は参照を代入します。
詳しい説明
参照型を、新しい変数に
1.代入した場合
2.MemberwiseCloneでオブジェクトを作成し、それを代入した場合。
1の場合は参照を代入しているので、参照したインスタンスが値型を持っている場合、代入先の変数がもつ値型を変更すると、元のインスタンスの値も変更される。
2の場合はインスタンス自体の参照は異なるので、値型を変更しても元のインスタンスが持つ値は変更されない。
参照元
ディスカッション
コメント一覧
まだ、コメントがありません