1. ホーム
  2. c#

C#では、foreachループの中で値型インスタンスのメンバを変更できないのはなぜですか?

2023-08-20 04:15:52

質問

値型は不変であるべきだということは知っていますが、それは単なる提案であって、規則ではありませんよね? では、なぜこのようなことができないのでしょうか。

struct MyStruct
{
    public string Name { get; set; }
}

 public class Program
{
    static void Main(string[] args)
    {
        MyStruct[] array = new MyStruct[] { new MyStruct { Name = "1" }, new MyStruct { Name = "2" } };
        foreach (var item in array)
        {
            item.Name = "3";
        }
        //for (int i = 0; i < array.Length; i++)
        //{
        //    array[i].Name = "3";
        //}

        Console.ReadLine();
    }
}

コード内のforeachループはコンパイルできませんが、コメントされたforループは問題なく動作します。エラーメッセージです。

item'は'foreach iteration variable'であるため、メンバを変更できません。

なぜでしょうか?

どのように解決するのですか?

foreach は列挙子を使用しており、列挙子は基になるコレクションを変更することはできないためですが はできます。 しかし、任意のオブジェクト によって参照される によって参照されるオブジェクトを変更します。ここで、ValueとReferenceタイプのセマンティクスが登場する。

参照型、つまりクラスでは、コレクションが保存しているのはオブジェクトへの参照だけです。そのため、コレクションはオブジェクトのどのメンバーにも実際に触れることはなく、それらについてあまり気にすることはありません。オブジェクトを変更しても、コレクションは変更されません。

一方、値型はコレクションにその構造全体を保存します。コレクションを変更し、列挙者を無効にすることなく、そのメンバーに触れることはできません。

さらに、列挙者が返す コピー を返します。ref型では、これは何の意味もありません。参照のコピーは同じ参照になり、参照されたオブジェクトを好きなように変更でき、その変更はスコープの外に広がります。一方、value-typeでは、オブジェクトのコピーしか得られないため、そのコピーに対する変更は決して伝搬しません。