質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

10705閲覧

c# forループの中で構造体のlistの値を変更できない理由を教えて

geitsu

総合スコア14

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

1グッド

1クリップ

投稿2017/05/10 13:53

リンク内容
上記のサイトに書いてあるようにc#ではforループの中で構造体のlistの値を変更できません
なぜできないのか理由を教えてください

juner👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

参照型でないためです。

C#

1struct SampleData 2{ 3 public bool Status; 4}

C#

1var sampleData = new List<SampleData>(); 2sampleData.Add(new SampleData()); 3for (int i = 0; i < sampleData.Count; i++) 4{ 5 sampleData[i].Status = true; 6}

上記の時、sampleData[i] は sampleData の i + 1 番目の要素のコピーを返します。コピーのメンバを変更してもオリジナルには影響しません。そのため、コピーのフィールドを変更しただけでこの値を使用しなかった場合、紛らわしいのでこれをエラーにします。
エラーにならなかった場合、一見正しいように見えるので非常に見つけにくいバグになりかねません。

次のようにして確かめてみてください。sampleData[i].Status は false で初期化されます。したがって、最初の Debug.WriteLine は false を出力します。しかし、次の Debug.WriteLine は true を出力します。

C#

1var sampleData = new List<SampleData>(); 2sampleData.Add(new SampleData()); 3for (int i = 0; i < sampleData.Count; i++) 4{ 5 var datum = sampleData[i]; 6 datum.Status = true; 7 Debug.WriteLine(sampleData[i].Status); 8 Debug.WriteLine(datum.Status); 9}

つまり、sampleData[i].Status = true; とした時、sampleData から i + 1 番目の要素のコピーが取り出され、その Status フィールドが true に変更されますが、取り出されたコピーはどこからも参照されることなく消えていくという意味のないコードになります。これをエラーとしてユーザーに明示的に注意喚起しているのです。

投稿2017/05/10 14:38

編集2017/05/10 15:11
Zuishin

総合スコア28662

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

geitsu

2017/05/10 15:12

ありがとうございます! 理解できました
guest

0

参照型だからではなくて List<T> のインデクサは 値を返すからでは? 配列は ref 戻り値を返せるので直接変更できます。

cs

1using System; 2using System.Collections.Generic; 3using System.Runtime.InteropServices; 4 5{ 6 ValueStruct[] array = [1,2,3]; 7 Console.WriteLine(string.Join(", ", array)); 8 array[0].Num = 10; 9 Console.WriteLine(string.Join(", ", array)); 10 // 配列のインデクサアクセスは ref 戻り値なので 参照を返す 11 ref var l = ref array[1]; 12 l.Num = 20; 13 Console.WriteLine(string.Join(", ", array)); 14} 15{ 16 List<ValueStruct> list = [1,2,3]; 17 Console.WriteLine(string.Join(", ", list)); 18 // // エラーになるので代入できない 19 // list[0].Num = 10; 20 // Console.WriteLine($"[{string.Join(", ", list)}"); 21 22 // 内部配列を Span<T> として取得する 23 var span = CollectionsMarshal.AsSpan(list); 24 span[1].Num = 10; 25 Console.WriteLine(string.Join(", ", list)); 26 27 // Span<T> のインデクサアクセスは ref 戻り値なので 参照を返す 28 ref var s = ref span[2]; 29 s.Num = 10; 30 Console.WriteLine(string.Join(", ", list)); 31} 32 33 34struct ValueStruct 35{ 36 public int Num {get;set;} 37 public ValueStruct(int Num) => this.Num = Num; 38 public override string ToString() => $"{nameof(ValueStruct)}{{{nameof(Num)} = {Num}}}"; 39 public static implicit operator ValueStruct(int num) => new(num); 40}

sharplab

ValueStruct{Num = 1}, ValueStruct{Num = 2}, ValueStruct{Num = 3} ValueStruct{Num = 10}, ValueStruct{Num = 2}, ValueStruct{Num = 3} ValueStruct{Num = 10}, ValueStruct{Num = 20}, ValueStruct{Num = 3} ValueStruct{Num = 1}, ValueStruct{Num = 2}, ValueStruct{Num = 3} ValueStruct{Num = 1}, ValueStruct{Num = 10}, ValueStruct{Num = 3} ValueStruct{Num = 1}, ValueStruct{Num = 10}, ValueStruct{Num = 10}

投稿2024/02/26 07:18

juner

総合スコア453

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問