struct(値型)とclass(参照型)のコピーの違い
structは値型、classは参照型です。代入や引数渡しのたびに値そのものがコピーされるか、参照が共有されるかが変わります。
なぜエラーが出ないのか
出力: 10
(エラーなし)- C#は何も報告しません。文法として正しいためです
出力: 10- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 class Point { ^ 2 public int X; 3 } 4 class Program { 5 static void Main(string[] args) { 6 Point p1 = new Point(); 7 p1.X = 5; 8 Point p2 = p1; 9 p2.X = 10; 10 System.Console.WriteLine(p1.X); 11 } 12 }
structは値型なので、代入すると中身がコピーされます。p2を変更してもp1には影響しません。
直し方: class を struct にします。
パターン2
1 class Counter { ^ 2 public int Count; 3 public void Increment() { Count++; } 4 } 5 class Program { 6 static void Main(string[] args) { 7 Counter c1 = new Counter(); 8 Counter c2 = c1; 9 c2.Increment(); 10 System.Console.WriteLine(c1.Count); 11 } 12 }
c2はc1のコピーです。c2.Increment()はコピーの方だけを変更するので、c1.Countは0のままです。
直し方: class を struct にします。
パターン3
1 class Item { ^ 2 public int Price; 3 } 4 class Program { 5 static void SetPrice(Item item) { item.Price = 999; } 6 static void Main(string[] args) { 7 Item i = new Item(); 8 i.Price = 100; 9 SetPrice(i); 10 System.Console.WriteLine(i.Price); 11 } 12 }
メソッドに値型を渡すと、引数はコピーになります。SetPrice内での変更は呼び出し元のiに反映されません。
直し方: class を struct にします。
パターン4
1 class Size { ^ 2 public int Width; 3 } 4 class Program { 5 static void Main(string[] args) { 6 Size s1 = new Size(); 7 s1.Width = 3; 8 Size s2 = s1; 9 s2.Width = 9; 10 System.Console.WriteLine(s1.Width); 11 } 12 }
structは値型なので、代入すると中身がコピーされます。s2を変更してもs1には影響しません。
直し方: class を struct にします。
パターン5
1 class Score { ^ 2 public int Value; 3 public void Add() { Value++; } 4 } 5 class Program { 6 static void Main(string[] args) { 7 Score a = new Score(); 8 Score b = a; 9 b.Add(); 10 System.Console.WriteLine(a.Value); 11 } 12 }
bはaのコピーです。b.Add()はコピーの方だけを変更するので、a.Valueは0のままです。
直し方: class を struct にします。
よくある誤解
見た目がほぼ同じclassとstructの宣言でも、コピーの意味は正反対です。変数への代入だけで挙動が変わるため、意図しない独立・共有を生みやすい部分です。
実務での勘所
structが用意されている実用上の理由は、ヒープへの割り当てとガベージコレクションの負荷を避けられることです。座標(x, y)のような小さく大量に作られるデータをclassにすると、生成のたびにヒープ確保とGCの対象が増えますが、structならスタック上や配列の中に直接値として並べられ、GCの負荷がかかりません。ただしコピーのコストは値のサイズに比例するため、Microsoftのガイドラインでは目安としておよそ16バイト以下の小さなデータにのみstructを使うことを推奨しています。