C#プログラミング備忘録

C# コレクションに構造体を格納するときに注意すべきこと

C#

例えばList<T>に構造体を格納するときには注意しないといけない点がある。以下のプログラムを考えてみる。

using System;
using System.Collections.Generic;

namespace CSharpStudy
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Book> list = new List<Book>() { new Book("坊ちゃん", "夏目漱石"),
                                                 new Book("人間失格", "太宰治")};

            list[0].Name = "吾輩は猫である"; //①コンパイルエラー CS1612
            
            list.ForEach(book => Console.WriteLine(book.Name));

            Console.ReadLine();

        }
    }

    struct Book
    {
        public string Name { get; set; }
        public string Author { get; set; }
        public Book(string name, string auther)
        {
            Name = name;
            Author = auther;
        }
    }
}

①の代入はコンパイルエラーとなる。理由はlist変数に格納されている構造体のインスタンスは値型のためコピーに対して代入を行っているから。詳細は以下を参照。

コンパイラ エラー CS1612
コンパイラ エラー CS1612

この問題を回避する方法は以下の通り。

using System;
using System.Collections.Generic;

namespace CSharpStudy
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Book> list = new List<Book>() { new Book("坊ちゃん", "夏目漱石"),
                                                 new Book("人間失格", "太宰治")};

            Book tmp = list[0];
            tmp.Name = "吾輩は猫である";
            list[0] = tmp;
            list.ForEach(book => Console.WriteLine(book.Name));

            Console.ReadLine();

        }
    }

    struct Book
    {
        public string Name { get; set; }
        public string Author { get; set; }
        public Book(string name, string auther)
        {
            Name = name;
            Author = auther;
        }
    }
}

このプログラムは正常にビルド出来て出力結果は以下の通りとなる。

吾輩は猫である
人間失格

あるいはBookを構造体ではなくクラスに変更しても良い。

using System;
using System.Collections.Generic;

namespace CSharpStudy
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Book> list = new List<Book>() { new Book("坊ちゃん", "夏目漱石"),
                                                 new Book("人間失格", "太宰治")};

            list[0].Name = "吾輩は猫である";
            
            list.ForEach(book => Console.WriteLine(book.Name));

            Console.ReadLine();

        }
    }

    //クラスに変更した
    class Book
    {
        public string Name { get; set; }
        public string Author { get; set; }
        public Book(string name, string auther)
        {
            Name = name;
            Author = auther;
        }
    }
}

このプログラムの出力結果は上と同じ。

吾輩は猫である
人間失格

コメント