インスタンス初期化ガイド:C#とUnityで使い分ける10のパターン

プログラムの信頼性は「いつ、どこで値が確定するか」を把握できるかどうかで大きく変わります。

C# と Unity を例に、代表的な 10 通りの初期化方法を 「クラス定義 + 1 行での生成コード」 付きで整理しました。

1. フィールド/プロパティ宣言時のインライン初期化

コード

class Player1
{
    private int _hp = 100;
    public string Name { get; set; } = "NoName";
}

var p1 = new Player1();   // _hp=100, Name="NoName"

解説

固定値やデフォルト値を最短で書きたいときに便利。実行時に変更される想定がない値に向きます。


2. コンストラクターでの必須値設定

コード

class Player2
{
    public int Hp  { get; }
    public string Name { get; }

    public Player2(int hp, string name)
    {
        Hp = hp;
        Name = name;
    }
}

var p2 = new Player2(80, "Hide");

解説

「未設定状態のインスタンス」を作らせたくないときは コンストラクターで必須パラメータを強制

オプション引数やコンストラクターチェーンで冗長さを減らせます。


3. オブジェクト初期化子 + init プロパティ

コード

class Player3
{
    public int Hp  { get; init; } = 100;
    public string Name { get; init; } = "NoName";
}

var p3 = new Player3 { Hp = 50, Name = "Mika" };

解説

初期化タイミングは保ちつつ、呼び出し側に JSON 風の書き味を提供。

init により 生成後の変更を禁止できるので不変条件を守れます。


4. レコード型と with 式

コード

public record Player4(int Hp = 100, string Name = "NoName");

var p4  = new Player4(120, "Boss");
var p4b = p4 with { Hp = 60 };   // p4 はそのまま

解説

イミュータブルオブジェクトを最短で宣言。with での差分コピーが強力で、設定オブジェクトや値オブジェクトに最適。


5. プライマリ コンストラクター(C# 12〜)

コード

public class Enemy5(int hp, string name)
{
    public int Hp   { get; } = hp;
    public string Name { get; } = name;
}

var e5 = new Enemy5(200, "Goblin");

解説

通常クラスでも “レコード並みのボイラープレート削減”。

コンストラクター引数をそのままフィールド・プロパティで使えるので可読性が高い。


6. 静的ファクトリーメソッド

コード

class Player6
{
    public int Hp { get; }
    public string Name { get; }

    private Player6(int hp, string name)
    {
        Hp = hp;
        Name = name;
    }

    public static Player6 CreateDefault() => new Player6(100, "Guest");
    public static Player6 CreateBoss()    => new Player6(500, "Demon King");
}

var p6a = Player6.CreateDefault();
var p6b = Player6.CreateBoss();

解説

生成ロジックに意味づけた名前を付けられるため、読み手の理解コストを下げられます。

複雑な初期化や DI コンテナ連携時にも活躍。


7. 依存性注入(DI)コンストラクター

コード

public interface IRandom { int Next(int max); }
public class SystemRandom : IRandom
{
    private readonly Random _rnd = new();
    public int Next(int max) => _rnd.Next(max);
}

class PlayerService7
{
    private readonly IRandom _random;
    public PlayerService7(IRandom random) => _random = random;
}

var service7 = new PlayerService7(new SystemRandom());

解説

外部サービスやランダム生成など テストで差し替えたい依存オブジェクトを注入。

モックを渡せばユニットテストが容易になります。


8. Unity:[SerializeField] と Awake/Start

コード

using UnityEngine;

public class Player8 : MonoBehaviour
{
    [SerializeField] int hp = 100;
    [SerializeField] string playerName = "Hero";

    void Start()
    {
        Debug.Log($"{playerName} starts with {hp} HP");
    }
}
/* 生成手順
   1. Hierarchy で空の GameObject を作成
   2. AddComponent<Player8>() を実行 or ドラッグ
*/

解説

デザイナがインスペクターで調整したい値を [SerializeField] に。

ランタイム依存の参照解決は Start()、軽量フィールド初期化はインライン/Awake() に分けると管理しやすいです。


9. コレクション初期化子

コード

enum Suit { Spade, Heart, Diamond, Club }
record Card9(Suit Suit, int Rank);

var deck9 = new List<Card9>
{
    new Card9(Suit.Spade, 1),
    new Card9(Suit.Heart, 13),
};

解説

Add を暗黙に呼ぶ糖衣構文。**「所有する複数オブジェクト」**をまとめて準備するときに重宝します。


10. JSON 逆シリアライズ

コード

using System.Text.Json;

public record Settings10(int Width, int Height, bool Fullscreen);

// settings.json: {"Width":1280,"Height":720,"Fullscreen":true}
var json  = File.ReadAllText("settings.json");
var cfg10 = JsonSerializer.Deserialize<Settings10>(json)!;

解説

設定ファイルや Web API から受け取った JSON を そのままオブジェクトへ復元

DTO をイミュータブルにしておくと、読み込んだ後の安全性が高まります。


まとめ:選択の指針

シチュエーションまず検討するパターン
必須値を強制したいコンストラクター (#2/#5)
差分更新しつつ不変レコード + with (#4)
Unity の調整用[SerializeField] (#8)
外部ファイル/API逆シリアライズ (#10)
テストダブル注入DI コンストラクター (#7)

値を「いつ」「どこで」確定させるかを意識すれば、バグの混入を最小限に抑え、テストも容易になります。

ぜひプロジェクトの規模やチーム構成に合わせて使い分けてみてください。

訪問数 3 回, 今日の訪問数 1回