【C#】シャローコピーとディープコピー

Qiita Advent Calendar 初参加です。よろしくお願いします。
最近学習していて面白いな、と感じたオブジェクトの複製についてまとめました。

## オブジェクトの複製パターン
オブジェクトの複製には2つのパターンがあります。

- **シャローコピー：簡易コピー**
参照のみをコピーし、実体を複製しない。
<font color="DeepPink">**複製するオブジェクトが値型か参照型かによって、コピー元（先）の変更がコピー先（元）に適用されるかどうかが異なる。**</font>
・<font color="MediumOrchid">**値型**</font>は値がそのまま複製される→ **変更が適用されない** 
・<font color="Orange">**参照型**</font>は参照先のアドレスが複製される→ **変更が適用される**
　※ただし <font color="Orange">**string型**</font> は変更のたびに別のインスタンスが生成されるため、**変更が適用されない**
 
https://learn.microsoft.com/ja-jp/dotnet/csharp/programming-guide/strings/#immutability-of-strings

- **ディープコピー：詳細コピー**
実体を複製する。
コピー元（元）を変更しても、コピー先（先）に変更が適用されない。

代入・コピーコンストラクタ・MemberwiseCloneメソッド・シリアライズ...etc など様々な方法で上記のコピーを実装することが可能です。今回は**シャローコピーを代入・コピーコンストラクタ・MemberwiseCloneメソッド、ディープコピーをMemberwiseCloneメソッド**で実装しました。また、コピー後に値を変更し、コピー元（またはコピー先）に影響があるかどうかを検証しました。

※今回の記事では値型と参照型をメソッド内でコピーするコードがありますが、引数を渡していないため<font color="DeepPink">**「参照渡し」は関係ありません。**</font>

https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/ref

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/e238c7b4-a90d-19bb-f625-9c9841d4ef59.png)

## 組み込み型メンバを持つ構造体をシャローコピーで複製する
組み込み型をメンバーとして `SampleStruct` 構造体を作成します。

```C#:SampleStruct 
    public struct SampleStruct
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public int[] Ids { get; set; }

        public string[] Names { get; set; }

        // 初期化
        public SampleStruct(int id, string name, int[] ids, string[] names)
        {
            Id = id;
            Name = name;
            Ids = ids;
            Names = names;
        }

        // コピーコンストラクタ
        public SampleStruct(SampleStruct sampleStruct)
        {
            Id = sampleStruct.Id;
            Name = sampleStruct.Name;
            Ids = sampleStruct.Ids;
            Names = sampleStruct.Names;
        }

        /// <summary>
        /// シャローコピーを実行します。
        /// </summary>
        /// <returns><see cref="SampleClass2" />が戻ります。</returns>
        public SampleStruct SharrowCopy()
        {
            return (SampleStruct)MemberwiseClone();
        }
    }
```

### 1. 代入
上記で作成した [`SampleStruct`](#組み込み型メンバを持つ構造体をシャローコピーで複製する) を「代入」で複製します。

```C#:代入
        public static void ShallowCopy()
        {
            var original = new SampleStruct(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C"});

            var copy = original; // 代入

            original.Id = 99;
            original.Name = "Hello, " + "World!";
            original.Ids[0] = 9;
            original.Names[0] = "あ";
        }
```
＜original（コピー元）の値を変更したあとの各変数＞
|  |original|copy||
|:--:|:--:|:--:|:--:|
| Id | 99 | 10 | 変更が適用されない |
| Name | Hello, World! | AAA | 変更が適用されない |
| Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |

### 2. コピーコンストラクタ
上記で作成した [`SampleStruct`](#組み込み型メンバを持つ構造体をシャローコピーで複製する) を「コピーコンストラクタ」で複製します。

```C#:コピーコンストラクタ
        public static void ShallowCopy2()
        {
            var original = new SampleStruct(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C"});

            var copy = new SampleStruct(original); // コピーコンストラクタ

            original.Id = 99;
            original.Name = "Hello, " + "World!";
            original.Ids[0] = 9;
            original.Names[0] = "あ";
        }
```
＜original（コピー元）の値を変更したあとの各変数＞
|  |original|copy||
|:--:|:--:|:--:|:--:|
| Id | 99 | 10 | 変更が適用されない |
| Name | Hello, World! | AAA | 変更が適用されない |
| Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |

### 3. MemberwiseCloneメソッド
上記で作成した [`SampleStruct`](#組み込み型メンバを持つ構造体をシャローコピーで複製する) を「MemberwiseCloneメソッド」で複製します。

```C#:MemberwiseCloneメソッド
        public static void ShallowCopy3()
        {
            var original = new SampleStruct(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C"});

            var copy = original.SharrowCopy(); // MemberwiseCloneメソッドを呼び出す

            original.Id = 99;
            original.Name = "Hello, " + "World!";
            original.Ids[0] = 9;
            original.Names[0] = "あ";
        }
```
＜original（コピー元）の値を変更したあとの各変数＞
|  |original|copy||
|:--:|:--:|:--:|:--:|
| Id | 99 | 10 | 変更が適用されない |
| Name | Hello, World! | AAA | 変更が適用されない |
| Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |

### 1, 2, 3の実行結果
値型 `Id` と string型 `Name` はコピー元のみ変更が適用されますが、参照型 `Ids` と `Names` は両方に変更が適用されます。

:white_check_mark: **構造体は値型のため、複製すると別のインスタンスが生成されます。**
![0_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/ff01838b-f7ce-bb5d-0d6d-73df596ffdd8.png)
![1_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/554ff317-3bd7-8331-842a-c9158a7207b2.png)

## 組み込み型メンバを持つクラスをシャローコピーで複製する
組み込み型をメンバーとして `SampleClass` クラスを作成します。
```C#:SampleClass
    public class SampleClass
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public int[] Ids { get; set; }

        public string[] Names { get; set; }

        // 初期化
        public SampleClass(int id, string name, int[] ids, string[] names)
        {
            Id = id;
            Name = name;
            Ids = ids;
            Names = names;
        }

        // コピーコンストラクタ
        public SampleClass(SampleClass sampleClass)
        {
            Id = sampleClass.Id;
            Name = sampleClass.Name;
            Ids = sampleClass.Ids;
            Names = sampleClass.Names;
        }

        /// <summary>
        /// シャローコピーを実行します。
        /// </summary>
        /// <returns><see cref="SampleClass" />が戻ります。</returns>
        public SampleClass SharrowCopy()
        {
            return (SampleClass)MemberwiseClone();
        }
    }
```
### 1. 代入
上記で作成した [`SampleClass`](#組み込み型メンバを持つクラスをシャローコピーで複製する) を「代入」で複製します。

```C#:代入
        public static void ShallowCopy()
        {
            var original = new SampleClass(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C" });

            var copy = original; // 代入
            
            original.Id = 99;
            original.Name = "Hello, " + "World!";
            original.Ids[0] = 9;
            original.Names[0] = "あ";
        }
```

＜original（コピー元）の値を変更したあとの各変数＞
|  |original|copy||
|:--:|:--:|:--:|:--:|
| Id | 99 | 99 | <font color="DeepPink">**変更が適用された**</font> |
| Name | Hello, World! | Hello, World! | <font color="DeepPink">**変更が適用された**</font> |
| Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |

### 1の実行結果
全ての型に変更が適用されます。

:white_check_mark: **クラスは参照型のため、代入すると参照アドレスが渡され同一インスタンスを参照します。**
![2_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/e5c5de14-d75e-ad52-f749-ecc31e30b70f.png)
![3_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/9243e7c2-4ee6-30fa-118f-209623073ba9.png)

### 2. コピーコンストラクタ
上記で作成した [`SampleClass`](#組み込み型メンバを持つクラスをシャローコピーで複製する) を「コピーコンストラクタ」で複製します。

```C#:コピーコンストラクタ
        public static void ShallowCopy2()
        {
            var original = new SampleClass(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C" });

            var copy = new SampleClass(original); // コピーコンストラクタ

            original.Id = 99;
            original.Name = "Hello, " + "World!";
            original.Ids[0] = 9;
            original.Names[0] = "あ";
        }
```
＜original（コピー元）の値を変更したあとの各変数＞
|  |original|copy||
|:--:|:--:|:--:|:--:|
| Id | 99 | 10 | 変更が適用されない |
| Name | Hello, World! | AAA | 変更が適用されない |
| Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |

### 3. MemberwiseCloneメソッド
上記で作成した [`SampleClass`](#組み込み型メンバを持つクラスをシャローコピーで複製する) を「MemberwiseCloneメソッド」で複製します。

```C#:MemberwiseCloneメソッド
        public static void ShallowCopy3()
        {
            var original = new SampleClass(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C" });

            var copy = original.SharrowCopy(); // MemberwiseCloneメソッドを呼び出す 

            original.Id = 99;
            original.Name = "Hello, " + "World!";
            original.Ids[0] = 9;
            original.Names[0] = "あ";
        }
```
＜original（コピー元）の値を変更したあとの各変数＞
|  |original|copy||
|:--:|:--:|:--:|:--:|
| Id | 99 | 10 | 変更が適用されない |
| Name | Hello, World! | AAA | 変更が適用されない |
| Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |

### 2, 3の実行結果
値型 `Id` と string型 `Name` はコピー元のみ変更が適用されますが、参照型 `Ids` と `Names` は両方に変更が適用されます。

:white_check_mark: **コピーコンストラクタ・MemberwiseCloneメソッドで複製すると<font color="DeepPink">別のインスタンスが生成されます。</font>**
![4_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/28503185-05ca-9b40-7253-44f5c217ec63.png)
![5_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/19004c62-7923-f40a-06e6-b032d52bf15a.png)

## クラスと構造体をメンバを持つ構造体をシャローコピーで複製する
先程使用した [`SampleStruct`](#組み込み型メンバを持つ構造体をシャローコピーで複製する)・[`SampleClass`](#組み込み型メンバを持つクラスをシャローコピーで複製する) をメンバーとして `SampleStruct2` を作成します。

```C#:SampleStruct2
    public struct SampleStruct2
    {
        public SampleStruct SampleStruct;

        public SampleClass SampleClass;

        // 初期化
        public SampleStruct2(SampleStruct sampleStruct, SampleClass sampleClass)
        {
            SampleStruct = sampleStruct;
            SampleClass = sampleClass;           
        }

        // コピーコンストラクタ
        public SampleStruct2(SampleStruct2 sampleStruct2)
        {
            SampleStruct = sampleStruct2.SampleStruct;
            SampleClass = sampleStruct2.SampleClass;
        }

        /// <summary>
        /// シャローコピーを実行します。
        /// </summary>
        /// <returns><see cref="SampleStruct2" />が戻ります。</returns>
        public SampleStruct2 SharrowCopy()
        {
            return (SampleStruct2)MemberwiseClone();
        }
    }
```

### 1. 代入
上記で作成した [`SampleStruct2`](#クラスと構造体をメンバを持つ構造体をシャローコピーで複製する) を「代入」で複製します。

```C#:代入
        public static void ShallowCopy()
        {
            // 構造体
            var sample1 = new SampleStruct(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C"});
            // クラス
            var sample2 = new SampleClass(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C" });

            var original = new SampleStruct2(sample1, sample2);
                                   
            var copy = original; // 代入
            
            // 構造体の値を変更     
            original.SampleStruct.Id = 99;
            original.SampleStruct.Name = "Hello, " + "World!";
            original.SampleStruct.Ids[0] = 9;
            original.SampleStruct.Names[0] = "あ";
            
            // クラスの値を変更
            original.SampleClass.Id = 99;
            original.SampleClass.Name = "Hello, " + "World!";
            original.SampleClass.Ids[0] = 9;
            original.SampleClass.Names[0] = "あ";
        }
```

＜original（コピー元）の値を変更したあとの各変数＞
|  |original|copy||
|:--:|:--:|:--:|:--:|
| SampleStruct.Id | 99 | 10 | 変更が適用されない |
| SampleStruct.Name | Hello, World! | AAA | 変更が適用されない |
| SampleStruct.Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| SampleStruct.Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Id | 99 | 99 | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Name | Hello, World! | Hello, World! | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |

### 2. コピーコンストラクタ
上記で作成した [`SampleStruct2`](#クラスと構造体をメンバを持つ構造体をシャローコピーで複製する) を「コピーコンストラクタ」で複製します。

```C#:コピーコンストラクタ
        public static void ShallowCopy2()
        {
            // 構造体
            var sample1 = new SampleStruct(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C"});
            // クラス
            var sample2 = new SampleClass(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C" });

            var original = new SampleStruct2(sample1, sample2);
                                   
            var copy = new SampleStruct2(original); // コピーコンストラクタ
            
            // 構造体の値を変更     
            original.SampleStruct.Id = 99;
            original.SampleStruct.Name = "Hello, " + "World!";
            original.SampleStruct.Ids[0] = 9;
            original.SampleStruct.Names[0] = "あ";
            
            // クラスの値を変更
            original.SampleClass.Id = 99;
            original.SampleClass.Name = "Hello, " + "World!";
            original.SampleClass.Ids[0] = 9;
            original.SampleClass.Names[0] = "あ";
        }
```

＜original（コピー元）の値を変更したあとの各変数＞
|  |original|copy||
|:--:|:--:|:--:|:--:|
| SampleStruct.Id | 99 | 10 | 変更が適用されない |
| SampleStruct.Name | Hello, World! | AAA | 変更が適用されない |
| SampleStruct.Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| SampleStruct.Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Id | 99 | 99 | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Name | Hello, World! | Hello, World! | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |

### 3. MemberwiseCloneメソッド
上記で作成した [`SampleStruct2`](#クラスと構造体をメンバを持つ構造体をシャローコピーで複製する) を「MemberwiseCloneメソッド」で複製します。

```C#:MemberwiseCloneメソッド
        public static void ShallowCopy3()
        {
            // 構造体
            var sample1 = new SampleStruct(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C"});
            // クラス
            var sample2 = new SampleClass(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C" });

            var original = new SampleStruct2(sample1, sample2);
                                   
            var copy = original.SharrowCopy(); // MemberwiseCloneメソッドを呼び出す
            
            // 構造体の値を変更     
            original.SampleStruct.Id = 99;
            original.SampleStruct.Name = "Hello, " + "World!";
            original.SampleStruct.Ids[0] = 9;
            original.SampleStruct.Names[0] = "あ";
            
            // クラスの値を変更
            original.SampleClass.Id = 99;
            original.SampleClass.Name = "Hello, " + "World!";
            original.SampleClass.Ids[0] = 9;
            original.SampleClass.Names[0] = "あ";
        }
```

＜original（コピー元）の値を変更したあとの各変数＞
|  |original|copy||
|:--:|:--:|:--:|:--:|
| SampleStruct.Id | 99 | 10 | 変更が適用されない |
| SampleStruct.Name | Hello, World! | AAA | 変更が適用されない |
| SampleStruct.Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| SampleStruct.Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Id | 99 | 99 | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Name | Hello, World! | Hello, World! | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |

### 1, 2, 3の実行結果
メンバー内の値型である **SampleStruct** は、値型 `Id` と string型 `Name` はコピー元のみ変更が適用されますが、参照型 `Ids` と `Names` は両方に変更が適用されます。
メンバー内の参照型である **SampleClass** は、全ての型に変更が適用されます。

:white_check_mark: **構造体は値型のため、複製すると別のインスタンスが生成されます。**
![6_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/7cd379e9-f00c-8626-1447-e07cfbffc834.png)
![7_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/5f882975-8fd6-54b3-c5e0-4d7b2455b09e.png)

## クラスと構造体をメンバを持つクラスをシャローコピーで複製する
先程使用した [`SampleStruct`](#組み込み型メンバを持つ構造体をシャローコピーで複製する)・[`SampleClass`](#組み込み型メンバを持つクラスをシャローコピーで複製する) をメンバーとして `SampleClass2` を作成します。

```C#:SampleClass2
    public class SampleClass2
    {
        public SampleStruct SampleStruct;

        public SampleClass SampleClass;

        // 初期化
        public SampleClass2(SampleStruct sampleStruct, SampleClass sampleClass)
        {
            SampleStruct = sampleStruct;
            SampleClass = sampleClass;           
        }

        // コピーコンストラクタ
        public SampleClass2(SampleClass2 sampleClass2)
        {
            SampleStruct = sampleClass2.SampleStruct;
            SampleClass = sampleClass2.SampleClass;
        }

        /// <summary>
        /// シャローコピーを実行します。
        /// </summary>
        /// <returns><see cref="SampleClass2" />が戻ります。</returns>
        public SampleClass2 SharrowCopy()
        {
            return (SampleClass2)MemberwiseClone();
        }
    }
```
### 1. 代入
上記で作成した [`SampleClass2`](#クラスと構造体をメンバを持つクラスをシャローコピーで複製する) を「代入」で複製します。

```C#:代入
        public static void ShallowCopy1()
        {
            // 構造体
            var sample1 = new SampleStruct(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C"});
            // クラス
            var sample2 = new SampleClass(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C" });

            var original = new SampleClass2(sample1, sample2);
                                   
            var copy = original; // 代入
            
            // 構造体の値を変更     
            original.SampleStruct.Id = 99;
            original.SampleStruct.Name = "Hello, " + "World!";
            original.SampleStruct.Ids[0] = 9;
            original.SampleStruct.Names[0] = "あ";
            
            // クラスの値を変更
            original.SampleClass.Id = 99;
            original.SampleClass.Name = "Hello, " + "World!";
            original.SampleClass.Ids[0] = 9;
            original.SampleClass.Names[0] = "あ";
        }                        
```

＜original（コピー元）の値を変更したあとの各変数＞
|  |original|copy||
|:--:|:--:|:--:|:--:|
| SampleStruct.Id | 99 | 99 | <font color="DeepPink">**変更が適用された**</font> |
| SampleStruct.Name | Hello, World! | Hello, World! | <font color="DeepPink">**変更が適用された**</font> |
| SampleStruct.Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| SampleStruct.Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Id | 99 | 99 | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Name | Hello, World! | Hello, World! | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |

### 1の実行結果
全ての型に変更が適用されます。

:white_check_mark: **クラスは参照型のため、代入すると参照アドレスが渡され同一インスタンスを参照します。**
![8_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/dc719460-c016-6931-0022-97937e540753.png)
![9_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/3d0af2cf-1ff0-590c-b2e9-957bca4f49a4.png)

### 2. コピーコンストラクタ
上記で作成した [`SampleClass2`](#クラスと構造体をメンバを持つクラスをシャローコピーで複製する) を「コピーコンストラクタ」で複製します。

```C#:コピーコンストラクタ
        public static void ShallowCopy2()
        {
            // 構造体
            var sample1 = new SampleStruct(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C"});
            // クラス
            var sample2 = new SampleClass(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C" });

            var original = new SampleClass2(sample1, sample2);
                                   
            var copy = new SampleClass2(original); // コピーコンストラクタ
            
            // 構造体の値を変更     
            original.SampleStruct.Id = 99;
            original.SampleStruct.Name = "Hello, " + "World!";
            original.SampleStruct.Ids[0] = 9;
            original.SampleStruct.Names[0] = "あ";
            
            // クラスの値を変更
            original.SampleClass.Id = 99;
            original.SampleClass.Name = "Hello, " + "World!";
            original.SampleClass.Ids[0] = 9;
            original.SampleClass.Names[0] = "あ";
        }                        
```

＜original（コピー元）の値を変更したあとの各変数＞
|  |original|copy||
|:--:|:--:|:--:|:--:|
| SampleStruct.Id | 99 | 10 |変更が適用されない |
| SampleStruct.Name | Hello, World! | AAA | 変更が適用されない |
| SampleStruct.Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| SampleStruct.Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Id | 99 | 99 | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Name | Hello, World! | Hello, World! | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |

### 3. MemberwiseCloneメソッド
上記で作成した [`SampleClass2`](#クラスと構造体をメンバを持つクラスをシャローコピーで複製する) を「MemberwiseCloneメソッド」で複製します。

```C#:MemberwiseCloneメソッド
        public static void ShallowCopy3()
        {
            // 構造体
            var sample1 = new SampleStruct(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C"});
            // クラス
            var sample2 = new SampleClass(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C" });

            var original = new SampleClass2(sample1, sample2);
                                   
            var copy = original.SharrowCopy(); // MemberwiseCloneメソッドを呼び出す
            
            // 構造体の値を変更     
            original.SampleStruct.Id = 99;
            original.SampleStruct.Name = "Hello, " + "World!";
            original.SampleStruct.Ids[0] = 9;
            original.SampleStruct.Names[0] = "あ";
            
            // クラスの値を変更
            original.SampleClass.Id = 99;
            original.SampleClass.Name = "Hello, " + "World!";
            original.SampleClass.Ids[0] = 9;
            original.SampleClass.Names[0] = "あ";
        }                        
```

＜original（コピー元）の値を変更したあとの各変数＞
|  |original|copy||
|:--:|:--:|:--:|:--:|
| SampleStruct.Id | 99 | 10 | 変更が適用されない |
| SampleStruct.Name | Hello, World! | AAA | 変更が適用されない |
| SampleStruct.Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| SampleStruct.Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Id | 99 | 99 | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Name | Hello, World! | Hello, World! | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Ids | 9, 2, 3 | 9, 2, 3 | <font color="DeepPink">**変更が適用された**</font> |
| SampleClass.Names | あ, B, C | あ, B, C | <font color="DeepPink">**変更が適用された**</font> |

### 2, 3の実行結果
メンバー内の値型である **SampleStruct** は、値型 `Id` と string型 `Name` はコピー元のみ変更が適用されますが、参照型 `Ids` と `Names` は両方に変更が適用されます。
メンバー内の参照型である **SampleClass** は、全ての型に変更が適用されます。

:white_check_mark: **コピーコンストラクタ・MemberwiseCloneメソッドで複製すると<font color="DeepPink">別のインスタンスが生成されます。</font>**
![10_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/b83560a5-3ded-d914-62cd-5d46e1e5996a.png)
![11_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/899ed3b3-1bde-5399-0420-493b548eda77.png)

## ディープコピーを実装する
代入・コピーコンストラクタ・MemberwiseCloneメソッドで複製すると、参照型メンバーが必ずシャローコピーになってしまいます。
その為オブジェクトのシャローコピーを作成した後、参照型メンバーをさらに複製する必要があります。

:white_check_mark: **メジャーで簡単な方法**
1. MemberwiseCloneメソッドを使用し、別のインスタンスを作成します。
2. 参照型メンバーがある場合は、そのメンバーを個別に複製します。

### 1. MemberwiseCloneメソッド
`SampleClass` というクラスを用意し、MemberwiseCloneメソッド Array.Cloneメソッド を使用して複製します。

```C#:SampleClass
    public class SampleClass
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public int[] Ids { get; set; }

        public string[] Names { get; set; }

        // 初期化
        public SampleClass(int id, string name, int[] ids, string[] names)
        {
            Id = id;
            Name = name;
            Ids = ids;
            Names = names;
        }

        /// <summary>
        /// シャローコピーを実行します。
        /// </summary>
        /// <returns><see cref="SampleClass" />が戻ります。</returns>
        public SampleClass SharrowCopy()
        {
            return (SampleClass) MemberwiseClone();
        }

        /// <summary>
        /// ディープコピーを実行します。
        /// </summary>
        /// <returns><see cref="SampleClass" />が戻ります。</returns>
        public SampleClass DeepCopy()
        {
            var clone = SharrowCopy();
            
            // Array.Clone の戻り値は Object のためキャストが必要
            if (clone.Ids != null)
            {
                clone.Ids = (int[]) this.Ids.Clone();
            }

            if (clone.Names != null)
            {
                clone.Names = (string[]) this.Names.Clone();
            }

            return clone;
        }
    }
```
初期化をし、ディープコピー用のメソッドを呼び出します

```C#:ディープコピー
        public static void DeepCopy()
        {
            var original = new SampleClass(10, "AAA", new[] { 1, 2, 3 }, new[] { "A", "B", "C"});
                                   
            var copy = original.DeepCopy();
            
            original.Id = 99;
            original.Name = "Hello, " + "World!";
            original.Ids[0] = 9;
            original.Names[0] = "あ";
        }
```
＜original（コピー元）の値を変更したあとの各変数＞
|  |original|copy||
|:--:|:--:|:--:|:--:|
| Id | 99 | 10 | 変更が適用されない |
| Name | Hello, World! | AAA | 変更が適用されない |
| Ids | 9, 2, 3 | 1, 2, 3 | 変更が適用されない |
| Names | あ, B, C | A, B, C | 変更が適用されない |

コピー元の変更が、コピー先に適用されていないことが分かります。

## まとめ
| 型の種類 | シャローコピー         | ディープコピー         |
|:-------:|:---------------------:|:---------------------:|
| 値型    | 別のインスタンスを参照 | 別のインスタンスを参照 |
| 参照型  | 同一インスタンスを参照 | 別のインスタンスを参照 |

- **<font color="MediumOrchid">値型</font> - 構造体：シャローコピー（代入・コピーコンストラクタ・MemberwiseCloneメソッド）**
コピー先がコピー元と別のインスタンスを参照している。
メンバにstring型以外の参照型が存在する場合、同一インスタンスを参照しているため、変更が適用される。
![7_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/5f882975-8fd6-54b3-c5e0-4d7b2455b09e.png)
- **<font color="Orange">参照型</font> - 注意点**
同じ参照型であっても、string型は変更するたびに別のインスタンスが生成される。

- **<font color="Orange">参照型</font> - クラス：シャローコピー（代入）**
コピー先がコピー元のインスタンスを参照している。
そのため、クラスメンバに値型が存在しても変更が適用される。
![9_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/3d0af2cf-1ff0-590c-b2e9-957bca4f49a4.png)

- **<font color="Orange">参照型</font> - クラス：シャローコピー（コピーコンストラクタ・MemberwiseCloneメソッド）**
コピー先がコピー元と別のインスタンスを参照している。
クラスメンバにstring型以外の参照型が存在する場合、同一インスタンスを参照しているため、変更が適用される。
クラスメンバが値型でも、その種類が構造体の場合、さらにそのメンバが値型か参照型かによって変更が適用されるかどうかが異なる。
![11_result_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566811/899ed3b3-1bde-5399-0420-493b548eda77.png)

記事内で使用したサンプルコードはこちら▶ [CSharpDuplicateObjectsSample](https://github.com/184ym2/til/tree/master/CSharpDuplicateObjectsSample)

## 参考URL
- [オブジェクトの複製](https://smdn.jp/programming/netfx/cloning/)
- [配列操作・複製](https://smdn.jp/programming/netfx/arrays/2_operations/#Array.Clone)
- [シャローコピーとディープコピー](https://programming.pc-note.net/csharp/copy.html)
- [Object.MemberwiseClone メソッド](https://docs.microsoft.com/ja-jp/dotnet/api/system.object.memberwiseclone?view=net-5.0)
- [Array.Clone メソッド](https://docs.microsoft.com/ja-jp/dotnet/api/system.array.clone?view=net-5.0)
- [文字列の不変性](https://learn.microsoft.com/ja-jp/dotnet/csharp/programming-guide/strings/#immutability-of-strings)

最後まで見て頂きありがとうございました。