C# - リストコピー(浅いコピー、深いコピー)

まず、浅いコピーでリストをコピーする方法について説明します。そして、浅いコピーで問題が発生する可能性がある状況と、深いコピーで解決する方法について説明します。

1. リストのコピー(浅いコピー)

浅いコピーでリストをコピーする方法を紹介します。

1.1 Listコンストラクタを使用した方法

以下の例のように new List<string>(list)でリストを作成するときは、引数としてリストを渡してコピーできます。

using System;

namespace Example {

    public class Program {

        public static void Main(string[] args) {

            List<string> list = new List<string>() {"a", "b", "c", "d"};

            List<string> copy = new List<string>(list);

            Console.WriteLine(string.Join(", ", list));
            Console.WriteLine(string.Join(", ", copy));
        }
    }
}

Output:

a, b, c, d
a, b, c, d

1.2 List.ToList() を使った方法

list.ToList() はコピーされたリストを返します。

List<string> list = new List<string>() {"a", "b", "c", "d"};

List<string> copy = list.ToList();

Console.WriteLine(string.Join(", ", list));
Console.WriteLine(string.Join(", ", copy));

Output:

a, b, c, d
a, b, c, d

1.3 List.GetRange() を使った方法

list.GetRange(index, count) は、リストの index から count 個数だけの要素を新しいリストに追加して返します。

以下のように GetRange() にリストをコピーできます。

List<string> list = new List<string>() {"a", "b", "c", "d"};

List<string> copy = list.GetRange(0, list.Count);

Console.WriteLine(string.Join(", ", list));
Console.WriteLine(string.Join(", ", copy));

Output:

a, b, c, d
a, b, c, d

2. 浅いコピーで発生する問題

string、intのような基本データ型ではなく、クラスオブジェクトを持っているリストのときに浅いコピーを使用したときに問題になることがあります。

たとえば、以下の例は

  • Person オブジェクトのリストを浅いコピーにコピーします。
  • コピーされたリストで Index 0Person オブジェクトの age を 0 に変更します。
  • 元のリストとコピーされたリストの内容を確認してみると、どちらも Johnage が 0 に変更されました。

たとえコピーはリストがオブジェクト情報だけをコピーして参照するため、オブジェクト内のデータはコピーされず、2つのリストが一緒に参照することになります。 このデータが変更されると、元のリストとコピーリストが参照するオブジェクト情報が一緒に変更されます。

using System;

namespace Example {
    public class Person {
        public string name;
        public int age;

        public Person(string name, int age) {
            this.name = name;
            this.age = age;
        }

        public override string ToString() {
            return "[" + name + ", " + age + "]";
        }
    }

    public class Program {

        public static void Main(string[] args) {

            List<Person> list = new List<Person>();
            list.Add(new Person("John", 30));
            list.Add(new Person("Patrick", 34));
            list.Add(new Person("Doe", 22));

            List<Person> copy = new List<Person>(list);
            copy.ElementAt(0).age = 0;

            Console.WriteLine(string.Join(", ", list));
            Console.WriteLine(string.Join(", ", copy));
        }
    }
}

Output:

[John, 0], [Patrick, 34], [Doe, 22]
[John, 0], [Patrick, 34], [Doe, 22]

3. リストコピー(深コピー)

List.ConvertAll(converter) はリストの要素を converter を使って別のデータに変更する関数です。

リストのすべての要素に対して以下のように Person オブジェクトを再生成して返すと、オブジェクト自体をコピーしたリストが生成されます。

  • ConvertAll(p => new Person(p.name, p.age))

オブジェクト自体をコピーしたため、元のリストとコピーされたリストは異なるオブジェクトを持っており、以下のように特定の要素の値を変更しても他のリストに影響を与えなくなります。

using System;

namespace Example {
    public class Person {
        public string name;
        public int age;

        public Person(string name, int age) {
            this.name = name;
            this.age = age;
        }

        public override string ToString() {
            return "[" + name + ", " + age + "]";
        }
    }

    public class Program {

        public static void Main(string[] args) {

            List<Person> list = new List<Person>();
            list.Add(new Person("John", 30));
            list.Add(new Person("Patrick", 34));
            list.Add(new Person("Doe", 22));

            List<Person> copy = list.ConvertAll(
                    p => new Person(p.name, p.age));
            copy.ElementAt(0).age = 0;

            Console.WriteLine(string.Join(", ", list));
            Console.WriteLine(string.Join(", ", copy));
        }
    }
}

Output:

[John, 30], [Patrick, 34], [Doe, 22]
[John, 0], [Patrick, 34], [Doe, 22]
codechachaCopyright ©2019 codechacha