Code Fix

中級

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  }
出力: 10

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  }
出力: 1

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  }
出力: 999

メソッドに値型を渡すと、引数はコピーになります。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  }
出力: 9

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  }
出力: 1

bはaのコピーです。b.Add()はコピーの方だけを変更するので、a.Valueは0のままです。

直し方: class を struct にします。

この問題を解いてみる →

よくある誤解

見た目がほぼ同じclassとstructの宣言でも、コピーの意味は正反対です。変数への代入だけで挙動が変わるため、意図しない独立・共有を生みやすい部分です。

実務での勘所

structが用意されている実用上の理由は、ヒープへの割り当てとガベージコレクションの負荷を避けられることです。座標(x, y)のような小さく大量に作られるデータをclassにすると、生成のたびにヒープ確保とGCの対象が増えますが、structならスタック上や配列の中に直接値として並べられ、GCの負荷がかかりません。ただしコピーのコストは値のサイズに比例するため、Microsoftのガイドラインでは目安としておよそ16バイト以下の小さなデータにのみstructを使うことを推奨しています。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)