C#構造体とクラスの違いと使い分け
初心者が知っておくべき基本と選び方の指針
はじめに
C#では、構造体(struct)とクラス(class)の両方を使って独自の型を定義できます。見た目は非常に似ていますが、挙動や使いどころが大きく異なるため、正しい理解が必要です。
本記事では、それぞれの特徴・違い・選び方の目安を、初心者にもわかりやすく解説します。
1. 構造体とクラスの定義
クラス(class)の例
class Player
{
public string Name;
public int HP;
}
構造体(struct)の例
struct Position
{
public float X;
public float Y;
}
見た目は似ていますが、使われ方やメモリの扱いが違います。
2. 大きな違い:値型と参照型
特徴 | 構造体(struct) | クラス(class) |
---|---|---|
メモリ分類 | 値型 | 参照型 |
保管場所 | スタック | ヒープ |
コピー | 値のコピー | 参照のコピー |
初期化 | newは省略可 | new必須 |
継承 | できない | 可能 |
値型とは?
値型は中身がコピーされるタイプ。int, float, boolなどと同じ。
Position a = new Position { X = 5, Y = 10 };
Position b = a; // 値をコピー
b.X = 20;
Console.WriteLine(a.X); // → 5(元は変わらない)
参照型とは?
参照型はアドレス(参照)をコピーするタイプ。
Player a = new Player { Name = "勇者", HP = 100 };
Player b = a; // 同じオブジェクトを参照
b.HP = 0;
Console.WriteLine(a.HP); // → 0(aも影響を受ける)
3. 構造体が向いているケース
- 小さくて軽いデータ(例:座標、色、サイズなど)
- 不変の値(immutable)に近い用途
- 頻繁なインスタンス生成・破棄が必要なケース
UnityのVector2, Color, Rectも構造体です。
4. クラスが向いているケース
- 状態の変化を持つオブジェクト
- 継承・ポリモーフィズムが必要な設計
- 多くのプロパティやメソッドを持つ複雑なオブジェクト
- 長期間に渡って管理・共有されるデータ
5. 構造体を使うときの注意点
- ボックス化(Boxing)が発生するとパフォーマンス低下の可能性あり→ object型やインターフェースに格納する際は注意
Position pos = new Position();
object obj = pos; // Boxing発生
- 参照が使えないため、ミューテーブルな設計は非推奨
「参照が使えないため、ミューテーブルな設計は非推奨」というのは、構造体(struct)を「値型」として使うときの特性に起因します。
1. 背景:構造体は「値型」
構造体は値型なので、コピーされて渡されるという特徴があります。
つまり、「呼び出し先で中身を書き換えても、呼び出し元には影響しない」のです。
2. ミューテーブル(mutable)とは?
ミューテーブル(mutable)とは、「変更可能な状態を持つ」ことを指します。
たとえば、以下のような構造体:
struct Counter
{
public int Value;
public void Increment()
{
Value++;
}
}
これは見た目は「変更できる」ように見えますが…
3. 参照が使えないとどうなる?
以下のように使用すると、期待通りに動かないことがあります:
Counter c = new Counter();
c.Increment();
Console.WriteLine(c.Value); // 期待: 1、実際: 0
これはなぜか?
4. 原因:メソッド呼び出し時にコピーされる
c.Increment() の呼び出し時に、c は値型(構造体)なので、Increment() メソッド内ではコピーが操作されているのです。
つまり、以下と同じ意味になります:
var temp = c; // コピー
temp.Value++; // コピー側の変更
// 元の c は変わらない
5. 結果:構造体での「状態変更」は混乱を招く
構造体がミューテーブル(状態変更可能)だと、ユーザーが意図した通りに動かないことがあるため、非推奨とされるのです。
6. 解決策:構造体はイミュータブル(immutable)にする
以下のように、「状態変更しない」設計が推奨されます:
struct Position
{
public float X { get; }
public float Y { get; }
public Position(float x, float y)
{
X = x;
Y = y;
}
public Position Move(float dx, float dy)
{
return new Position(X + dx, Y + dy); // 新しい構造体を返す
}
}
使用例:
Position p1 = new Position(1, 2);
Position p2 = p1.Move(1, 1); // p1はそのまま、p2は新しい座標
まとめ
理由 | 結論 |
---|---|
値型はコピーされる | メソッドでの変更が元に影響しない |
意図せず「変更されない」ように見える | バグや混乱の元になる |
そのため構造体は「変更不可設計」にするのが安全 | イミュータブル構造体推奨 |
このような背景から、構造体を「状態変更あり(ミューテーブル)」にするのは避けた方がよいというのが、C#の設計上のベストプラクティスです
6. 使い分けの目安(まとめ)
判断ポイント | 構造体(struct) | クラス(class) |
---|---|---|
データのサイズが小さい | ◎ | △ |
不変にしたい | ◎ | ○ |
状態が変わる | △ | ◎ |
継承したい | × | ◎ |
Unityで使う座標系 | ◎(Vector3など) | – |
7. よくある質問
Q. 構造体でもnewを使うのはなぜ?
A. 構造体はnewを使わなくても使えますが、newを使うことですべてのフィールドが0で初期化された状態になります。
Position p; // 初期化されていない → 使用不可
Position p2 = new Position(); // OK(X=0, Y=0)
おわりに
構造体とクラスは似て非なるもの。それぞれの性質を理解し、適材適所で使い分けることがC#プログラミングの第一歩です。
とくにUnity開発では、構造体が内部でどう扱われるかを意識すると、パフォーマンス改善にも繋がります。
関連リンク
このブログは初学者でも理解できるよう、例を交えながら明確に構造体とクラスの違いを説明しました。
ディスカッション
コメント一覧
まだ、コメントがありません