スポンサーリンク
設計・アーキテクチャ

Unityでリフレクションを活用!柔軟で拡張性の高いスクリプトを作る方法

設計・アーキテクチャ

はじめに

Unityでゲーム開発を続けていると、あるタイミングでこんな違和感を覚えることはありませんか?

  • スキルやギミックを追加するたびに ifswitch が増えていく
  • Managerクラスがどんどん巨大化して、触るのが怖くなる
  • 「これ、あとから拡張できる設計なのかな?」と不安になる

C#としては間違っていない。ちゃんと動く。
でも「設計としてこのままでいいのか?」と感じ始めたとき、多くの人が検索するのが「Unity リフレクション」だったりします。

ただ、リフレクションについて調べると、
「危険」「遅い」「使うべきじゃない」「上級者向け」
といった情報も多く、結局こう思ってしまいがちです。

「なんとなく怖いけど、今の設計もしんどい…」

この記事は、そんな“使うか迷っている段階”の人に向けて書いています。

単なるAPI解説やサンプル集ではありません。
なぜリフレクションを検討する段階に来るのか
どんな場面なら使ってよくて、どんな場面では使うべきでないのか
ScriptableObjectやイベント設計とどう使い分けるのか

こういった「設計判断の軸」を、Unity開発の文脈で整理していきます。

「switch文を減らしたいから使う」のではなく、
「将来の拡張に耐えられる設計を選ぶ結果として、リフレクションが候補に上がる」
そんな理解を持ち帰ってもらえることを目標にしています。




結論

先に結論からお伝えします。

Unityにおけるリフレクションは、「switch文の代替テクニック」ではありません
本質は、コードを修正せずに振る舞いを追加・差し替えられる状態を作ることにあります。

そのため、リフレクションを使ってよいのは次のような場面です。

  • 将来、どんな機能が追加されるかがまだ確定していない
  • 機能追加のたびに既存コードを修正したくない
  • クラスや処理を「後から差し込める拡張ポイント」を作りたい

逆に言うと、 「とりあえずswitch文が長いから」 「if文が汚くなってきたから」 という理由だけでリフレクションを導入すると、ほぼ確実に失敗します。

また、Unityにはすでに
ScriptableObjectによるデータ駆動設計
C#イベントやActionによる疎結合設計
ステートマシンやデザインパターン
といった強力な選択肢があります。

それらで解決できる問題を、あえてリフレクションで解決する必要はありません。

この記事では、
「まず他の設計手法を検討し、それでも足りない場合にリフレクションを選ぶ」
という安全で現実的な判断プロセスを、具体例と一緒に解説していきます。




なぜリフレクションを検討する段階に来るのか(問題提起)

switch文・if文が増え続ける構造的な理由

Unityでスキルシステムやギミック管理、イベント処理を実装していると、 最初はシンプルだったはずのコードが、気づけばこんな形になりがちです。

switch (skillType)
{
    case SkillType.Fire:
        UseFire();
        break;
    case SkillType.Ice:
        UseIce();
        break;
    case SkillType.Thunder:
        UseThunder();
        break;
    // さらに増え続ける…
}

このコード自体は、文法的にも動作的にも正しいです。 ただ問題は、スキルが増えるたびに、このswitch文を修正し続ける必要がある点にあります。

つまり、

  • 機能追加 = 既存コードの修正
  • 拡張するほど影響範囲が広がる
  • 修正のたびにデグレードのリスクが増える

という構造になってしまっているわけです。

設計が硬直すると何が起きるか

この状態が続くと、コードは次第に「壊れやすいもの」に変わっていきます。

  • 新機能を追加するのが怖くなる
  • 一部を直しただけなのに別の機能が壊れる
  • 「ここは触らない方がいい」というブラックボックスが生まれる

これはスキル不足ではなく、設計上の責務が集中しすぎていることが原因です。

この考え方は、SOLID原則、とくに「単一責務の原則」や「開放閉鎖の原則」と強く関係しています。

設計の基本的な考え方については、以下の記事で詳しく解説しています。

こうした「拡張するほど苦しくなる構造」に直面したとき、 はじめて候補として浮かび上がってくるのが、 「コードを修正せずに振る舞いを増やす仕組み」です。

リフレクションは、まさにこの要求に応えるための選択肢の一つとして登場します。
ただし、この時点ではまだ「候補の一つ」でしかありません。




リフレクションとは何か(設計視点での再定義)

APIとしてのリフレクション(最低限の理解)

リフレクション(Reflection)は、C#が持つ型情報を実行時に扱うための仕組みです。 Unityに限らず、.NET全体で使える機能ですね。

具体的には、

  • クラスやメソッドの情報を取得する
  • 文字列や型情報からメソッドを呼び出す
  • 実行時にインスタンスを生成する

といったことが可能になります。

技術的な解説だけを見ると、 「動的にメソッドを呼べる便利機能」 として紹介されがちですが、 それだけの理解で使うと設計が破綻しやすいのがリフレクションの難しいところです。

設計視点で見るリフレクションの本質

設計の観点で見ると、リフレクションの本質はとてもシンプルです。

「依存関係を実行時まで確定させない」

通常のコードでは、
「このクラスは、このクラスを呼ぶ」
という関係がコンパイル時に確定します。

一方、リフレクションを使うと、

  • どのクラスを使うか
  • どのメソッドを呼ぶか

実行時に決めることができます。

これはつまり、 「後から振る舞いを差し替えられる余地を設計に組み込む」 ということです。

ここで重要なのは、 リフレクションが「設計を柔らかくする魔法」ではないという点です。

設計上の拡張ポイントが存在しない状態でリフレクションを導入しても、 ただ読みにくく、壊れやすいコードが生まれるだけです。

逆に、
「ここは将来、振る舞いが増える」
「ここはプラグイン的に差し替えたい」
というポイントが明確に決まっている場合、 リフレクションは非常に強力な道具になります。

次の章では、 実際にswitch文が増えていく設計と、 拡張しやすい設計を比較しながら、 どこで判断が分かれるのかを見ていきます。




実践:switch文を減らす設計アプローチ

悪い例:switch文にすべてを集約した設計

まずは、よくある「動くけどつらくなる」設計から見てみましょう。

スキルやギミックの種類に応じて、Managerクラスで分岐させるパターンです。

public void Execute(string command)
{
    switch (command)
    {
        case "Fire":
            Fire();
            break;
        case "Ice":
            Ice();
            break;
        case "Heal":
            Heal();
            break;
    }
}

このコードの問題点は、処理内容そのものではありません。

  • 新しい処理を追加するたびに、このクラスを修正する必要がある
  • Managerが「すべての処理内容」を知ってしまっている
  • テスト・修正・拡張の影響範囲が常に広い

つまりこのクラスは、 「判断」と「実行」の両方を抱え込んでいる状態です。

良い例:責務を分離した設計

次に、設計の視点を変えてみます。

「何を実行するか」を決める側と、 「どう実行するか」を分けて考えます。

public interface ICommand
{
    void Execute();
}
public class FireCommand : ICommand
{
    public void Execute()
    {
        // Fireの処理
    }
}

実行側は、「ICommandを実行する」ことだけを知っていれば十分です。

public void Execute(ICommand command)
{
    command.Execute();
}

この構造にすると、

  • 新しい処理はクラスを追加するだけ
  • 既存コードを修正しない
  • 責務が明確でテストもしやすい

という状態になります。

そしてここで初めて、 「どのCommandを使うかを実行時に決めたい」 という要求が自然に出てきます。

この「選択部分」に対して、リフレクションを使う余地が生まれるわけです。

こうした「良い設計/悪い設計」の違いを、 理屈ではなくコードの差で理解したい場合は、 次の書籍がとても参考になります。

良いコード/悪いコードで学ぶ設計入門
✅ Amazonでチェックする✅ 楽天でチェックする

リフレクションは、このように 「設計が整理されたあと、最後のピースとして使う技術」 だと考えると、失敗しにくくなります。




リフレクション × 設計パターンという考え方

ここまで見てきたように、リフレクションは単体で使う技術ではありません。
設計パターンと組み合わせて初めて、「拡張に強い構造」として意味を持ちます。

Strategyパターンとの組み合わせ

Strategyパターンは、 「振る舞いをクラスとして分離し、差し替え可能にする」 ための設計パターンです。

switch文で処理を切り替えている場合、 その分岐条件そのものが「戦略」になっています。
Strategyパターンでは、この戦略をクラスとして外に出すのがポイントです。

たとえば、

  • 攻撃方法の違い
  • 移動ロジックの違い
  • AIの判断ロジック

こうした「同じ役割だが中身が違う処理」は、 Strategyとして切り出すことで、 switch文を一切書かずに拡張できるようになります。

そして、どのStrategyを使うかを 実行時に決めたい場合に、 リフレクションが選択肢として自然に浮かび上がります。

Commandパターンとの相性

先ほどの例でも少し触れましたが、 リフレクションはCommandパターンとも非常に相性が良いです。

Commandパターンでは、 「何をするか」をオブジェクトとして表現します。

  • スキル発動
  • ボタン操作
  • イベント処理

これらをCommandとして切り出すことで、 実行側は「Commandを実行する」ことだけを知っていればよくなります。

ここでリフレクションを使えば、
クラス名や設定値からCommandを生成する
プラグイン的にCommandを追加する
といった設計も可能になります。

この考え方は、 ステートマシン設計とも非常に近い関係にあります。

状態ごとに振る舞いを分離する設計については、 以下の記事も参考になります。

重要なのは、 「まずパターンで整理できているか」 という点です。

パターンで整理できていない状態で リフレクションを導入すると、 ただ分かりにくいコードになるだけなので注意が必要です。




ScriptableObject・イベント設計との使い分け

ここまで読んで、
「それ、リフレクションじゃなくてもScriptableObjectやイベントでできるのでは?」
と感じた方もいるかもしれません。

その感覚はかなり正しいです。
Unityでは、リフレクションは最後に検討する選択肢であり、 先に検討すべき設計手法がいくつも存在します。

ScriptableObjectが向いているケース

ScriptableObjectは、 データを中心に振る舞いを切り替えたい場合に非常に強力です。

  • スキルの数値や設定をInspectorから調整したい
  • プログラマ以外も調整に関わる
  • 実行時にクラス構造自体は変わらない

こうしたケースでは、 リフレクションを使う理由はほとんどありません。

ScriptableObjectを使ったデータ駆動設計については、 以下の記事で詳しく解説しています。

イベント設計が向いているケース

次に、イベント設計です。

C#のEventやActionを使ったイベント駆動設計は、 「通知」と「反応」を分離したい場合に向いています。

  • 何が起きたかだけを伝えたい
  • 誰が処理するかは知らなくていい
  • 複数の処理が同時に反応する

この場合も、 実行する処理を動的に切り替えたいわけではないため、 リフレクションは不要です。

Unityにおけるイベント設計については、 次の記事が参考になります。

それでもリフレクションを選ぶべきケース

では、どんな場合にリフレクションが選択肢になるのでしょうか。

それは、 「クラス構造そのものを後から拡張したい場合」です。

  • 後から新しい処理クラスを追加したい
  • ビルド後に振る舞いを差し替えたい
  • プラグイン的な拡張ポイントを用意したい

ScriptableObjectは「データの差し替え」、 イベントは「通知の分離」、 リフレクションは「構造の拡張」。

この役割分担を意識すると、 技術選定で迷いにくくなります。




使ってはいけない場面・注意点(Unity特有)

リフレクションは強力ですが、 Unityでは特に注意すべき落とし穴がいくつかあります。

ここを理解せずに使うと、 「動くけど本番で壊れる」「重くて使い物にならない」 といった事故につながりやすいので要注意です。

Update内での使用はほぼNG

まず一番分かりやすい注意点が、パフォーマンスです。

リフレクションによるメソッド呼び出しは、 通常のメソッド呼び出しに比べてオーバーヘッドが大きいです。

そのため、

  • Update / FixedUpdate 内で毎フレーム呼ぶ
  • 大量のオブジェクトに対して反復実行する

といった使い方は、基本的に避けるべきです。

実践では、 初期化時に一度だけリフレクションを使い、結果をキャッシュする という形に落ち着くことがほとんどです。

IL2CPPとコードストリッピングの問題

Unityでリフレクションを語る上で避けて通れないのが、 IL2CPP環境での挙動です。

IL2CPPでは、 ビルド時にC#コードがC++に変換される(AOTコンパイル)ため、 「参照されていない」と判断されたコードは削除されます。

問題は、 リフレクション経由でしか使われていないクラスやメソッドが、 Unityから見ると「未使用」に見えてしまう点です。

その結果、
エディタでは動くのに、ビルド後に実行時エラーが出る
という、非常に分かりづらいトラブルが起こります。

対策:使われていることを明示する

この問題への代表的な対策は次のとおりです。

  • link.xml でクラス・メソッドを保持する
  • Preserve 属性を使って削除を防ぐ
  • 可能であれば、明示的な参照を一度だけ通す

ただし、これらは 「そもそもリフレクションが必要か?」 を再検討するサインでもあります。

型安全性が失われるという現実

もう一つ重要なのが、型安全性の問題です。

文字列でクラス名やメソッド名を指定する場合、 コンパイル時のチェックが一切効きません。

  • 名前変更で壊れる
  • 引数の不一致に気づけない
  • エラーが実行時まで分からない

だからこそ、 設計段階で責務を整理し、使用箇所を限定する ことが非常に重要になります。




よくある誤解・設計ミス

リフレクションは情報量が多く、 誤解されたまま使われやすい技術でもあります。

ここでは、Unity開発で特によく見かける 「やりがちな勘違い」を整理しておきます。

誤解1:リフレクションは上級者向けで危険な技術

確かに、無計画に使うと危険です。
しかしそれは、リフレクションに限った話ではありません。

問題なのは技術そのものではなく、 「なぜそれを使うのかが整理されていないこと」です。

設計上の拡張ポイントが明確であれば、 リフレクションは安全に扱うことができます。

誤解2:switch文はすべて悪

switch文自体が悪いわけではありません。

問題になるのは、 「増え続けることが前提なのに、1か所に集約されている」 状態です。

種類がほぼ増えない処理や、 ローカルな分岐であれば、 switch文は今でも十分に有効な選択肢です。

誤解3:switchを全部リフレクションに置き換えれば解決

これは最も危険な誤解です。

分岐をリフレクションに置き換えただけでは、 設計は何も改善されません。

責務が整理されていない構造のまま リフレクションを導入すると、 読みにくさとデバッグ難易度だけが跳ね上がります。

誤解4:とりあえず動いているから問題ない

エディタ上で動いていることと、 長期運用できる設計であることは別です。

特にリフレクションは、
ビルド環境
プラットフォーム
チーム規模
によって、問題が表面化するタイミングが大きく変わります。

「今は大丈夫」ではなく、 「将来どうなるか」を基準に判断することが重要です。




まとめ

Unityにおけるリフレクションは、 便利か危険かで語る技術ではありません。

本質は、 「コードを修正せずに拡張できる余地を、設計として用意するための手段」 です。

switch文やif文が増えすぎて苦しくなったとき、 いきなりリフレクションに飛びつくのではなく、

  • 責務は分離できているか
  • StrategyやCommandとして切り出せないか
  • ScriptableObjectやイベント設計で十分ではないか

こうした選択肢を一度整理したうえで、 それでも足りない部分を補うために使うのが、 失敗しにくいリフレクションの使い方です。

設計が整理されたあとに導入されたリフレクションは、
・修正に強く
・拡張に耐え
・長期運用しやすい
コードベースを支える、心強い道具になります。

「switch文がつらい」と感じ始めた今こそ、 技術そのものではなく、 設計の選び方を見直すタイミングです。


参考文献


よくある質問(FAQ)

Q
switch文は全部悪なのでしょうか?
A

いいえ、そんなことはありません。

種類がほぼ増えない処理や、 ローカルに完結する分岐であれば、 switch文は今でも分かりやすく有効です。

問題になるのは、 将来増え続けることが分かっている処理を、1か所で抱え込んでいる場合 です。

Q
リフレクションはパフォーマンス的に本当に危険ですか?
A

使い方次第です。

Update内で毎フレーム使うような設計は避けるべきですが、 初期化時や登録処理など、 実行回数を限定した使い方であれば、 実務上問題になるケースは多くありません。

Q
小規模なゲームでもリフレクションを使う価値はありますか?
A

多くの場合、不要です。

小規模なプロジェクトでは、 ScriptableObjectやイベント設計、 シンプルなパターン設計で十分なことがほとんどです。

リフレクションは、 「将来の拡張がほぼ確実に発生する」 と見えてきた段階で検討するくらいが、ちょうど良い選択です。

※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。

※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。

スポンサーリンク