インスタンス初期化ガイド: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) |
値を「いつ」「どこで」確定させるかを意識すれば、バグの混入を最小限に抑え、テストも容易になります。
ぜひプロジェクトの規模やチーム構成に合わせて使い分けてみてください。
ディスカッション
コメント一覧
まだ、コメントがありません