構造体メンバへの代入を行おうとしたところ、CS1612エラーが発生しました。
下記サイトを読んだのですが、いまいちはっきり理解ができず、フォローをいただきたいです。
https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/compiler-messages/cs1612
一旦私の解釈を書きます。
C#
1① 2List<MyStruct> list = {…}; 3list[0].Name = "MyStruct42"; //CS1612
まず↑でCS1612エラーとなる理由
list[0].Name部分はlist[0].NameのGetterから取得してきた値そのものであるため
何らかの値をいれることはできない。
C#
1② 2List<MyStruct> list = {…}; 3MyStruct ms = list[0]; 4ms.Name = "MyStruct42"; 5list[0] = ms;
↑でエラーとならない理由
ここがよくわかりません。
②でも①と同様にms.NameのGetterから取り出した値に対して
代入をしているように見えてしまいます。
アドバイスお願いいたします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答6件
0
こんにちは。
まず、① のコードは ② と等価ではありません。
① のコードを ② っぽく書くと以下のようになります。
csharp
1List<MyStruct> list = {…}; 2 3{ 4 MyStruct ms = list[0]; 5 ms.Name = "MyStruct42"; 6}
② との違いは、list
に入れ直す処理 list[0] = ms;
が無いことです。
せっかく ms
に書き込んだのに、それを何もせず捨てているわけですね。
list
の中身は変更されることはなく、書いたコードに意味はありません。
なので、そういう罠を踏まないように、わざわざエラーにしてくれているのです。親切ですね。
投稿2024/02/15 06:34
総合スコア4110
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
MyStruct は構造体(=値型) なので、list[0].Name
の段階で list[0]
からのコピーの MyStruct.Name
が呼ばれている為、そこに代入しても意味が無いというエラーです。
list[0]
を直接修正したい場合は次の様に CollectionsMarshal.AsSpan()
すると List<T>
の内部配列を取得でき、そこから インデクサアクセスにより list[0]
内の参照が取得できる為、戻し忘れみたいなこともないのでおすすめします。(こういう時に配列を使うと list[0]
がref 戻り値なので直接 参照が取得できるので楽でもあります。
csharp
1using System; 2using System.Collections.Generic; 3using System.Runtime.InteropServices; 4List<MyStruct> list = [ new("a"), new("b"), new("c")]; 5 6Console.WriteLine($"[{string.Join(", ", list)}]"); 7 8// 内部配列を取得 9var span = CollectionsMarshal.AsSpan(list); 10// 変更 11span[0].Name = "MyStruct42"; 12 13Console.WriteLine($"[{string.Join(", ", list)}]"); 14 15 16struct MyStruct { 17 public MyStruct(string name) => Name = name; 18 public string Name; 19 public override string ToString() => $"{nameof(MyStruct)}{{ {nameof(Name)} = {Name}}}"; 20}
実行結果
text
1[MyStruct{ Name = a}, MyStruct{ Name = b}, MyStruct{ Name = c}] 2[MyStruct{ Name = MyStruct42}, MyStruct{ Name = b}, MyStruct{ Name = c}]
蛇足
配列ならこう
csharp
1using System; 2MyStruct[] list = [ new("a"), new("b"), new("c")]; 3 4Console.WriteLine($"[{string.Join(", ", list)}]"); 5 6// 変更 7list[0].Name = "MyStruct42"; 8 9Console.WriteLine($"[{string.Join(", ", list)}]"); 10 11struct MyStruct { 12 public MyStruct(string name) => Name = name; 13 public string Name; 14 public override string ToString() => $"{nameof(MyStruct)}{{ {nameof(Name)} = {Name}}}"; 15}
投稿2024/02/15 08:07
編集2024/02/26 04:47総合スコア128
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
関数の戻り値とはreturnに指定された変数などが持つ値のコピーを指します
参照型(class)と値型(strict)では、このコピーされる値が異なります
参照型の場合、戻り値にコピーされる値はインスタンスの場所を指し示すアドレスなので、returnに指定された値と同一のインスタンスを指すことができます
戻り値経由でインスタンスのメンバ値を変更できるのはこのためです
一方で値型の場合はreturnに指定される値がオブジェクトのインスタンスそのものなので戻り値もそのコピーとなります
この時、returnに指定されたオブジェクトは関数の終了によって変数などと共に解放されます
よって戻り値経由でインスタンスへアクセスしても、それは元のインスタンスとは異なる上、オリジナルが存在しません
変更の反映先が不在となるため、このような操作はコンパイラにより無意味と判定されてエラーとなります
そして今回、listを通じて返されているのはNameという名のプロパティです
プロパティはget
とset
の二つのメソッドで構成される関数の一種です
よってプロパティから返される値はget
でreturnされたインスタンスのコピーとなります
さて、これを踏まえて以下のコードを解剖します
C#
1② 2List<MyStruct> list = {…}; 3MyStruct ms = list[0]; 4ms.Name = "MyStruct42"; 5list[0] = ms;
先程プロパティはget
とset
の二つのメソッドを持つと紹介しました
ms.Name = "MyStruct42"
はNameプロパティのset
に値を渡す宣言です
set
にはvalue
という引数があります
Nameに代入された値は全てset
メソッドで管理されます
MyStruct ms = list[0];
は新規に型宣言された変数msにlist[0]
が持つインスタンスのコピーを渡す宣言です
そしてlist[0] = ms
ではlist[0]
にmsが持つインスタンスをコピーしています
つまりlist[0]
からコピーして編集を加えたインスタンスを再びlist[0]
にコピーし直しています
ここで行われていることは全てコピーによる値の取得と上書きなので、エラーになる要素がありません
投稿2024/02/15 10:52
編集2024/02/15 10:56総合スコア25
0
この問題のポイントは、MyStructが、クラスではなく、構造体であることです。
その為、MyStruct ms = list[0]; は、参照ではなく値のコピーとなります。
これは、Listとは別の場所なので、ms.Name = "MyStruct42"; で代入しても、CS1612は発生しません。
なお、別の場所での代入なので、最後の list[0] = ms; を忘れると、List内は変更されません。
投稿2024/02/15 07:03
総合スコア1708
0
list[0].Name部分はlist[0].NameのGetterから取得してきた値
②でも①と同様にms.NameのGetterから取り出した値
どういう意味で言っているのかよくわかりませんが,
問題は Name
ではなく list[0]
の側です.
ここでは list
の要素が値側なので list[0]
は list
に入っている最初の要素そのものではなくてそのコピーを返す,という話です.
返ってきたコピーを全く使うそぶりが無いのにそのコピーの中身を変更しても意味が無い,という旨のエラーでしょう.
投稿2024/02/15 06:58
総合スコア11658
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
まずエラーとなるほうですが
Listに格納されている構造体をA
とします。
そこでListからlist[0]
と構造体を取得する時に実際に格納されているA
を複製したA'
が返されます。
そうなるとlist[0].Name = "MyStruct42";
というのはA'.Name = "MyStruct42";
となり、そのままA'
という構造体は保持されていないので消えてしまいます。値を変数に受けずにそのものを変更しようとしているのでエラーとなります。
エラーとならないほうですが
MyStruct ms = list[0];
と複製されたA'
がmsという変数に格納されます。
その後、ms.Name = "MyStruct42";
とA'
の内容が変更されます。
そしてlist[0] = ms;
とListにA'
を代入しようとします。この際A'
が複製されListにはA''
が格納されることになります。
投稿2024/02/15 06:56
総合スコア10258
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。