「リフレクションを使えば柔軟な実装ができる」と聞いたことはあっても、実際にどう書けばいいのか、そしてどこまで安全に使っていいのか、迷ってしまうことはありませんか。
特に「パフォーマンスが落ちる」という話も耳にすると、自分のプロジェクトで使っても大丈夫なのか不安になりますよね。
この記事では、Unity C#でリフレクションを使うための基本的な書き方から、BindingFlagsの使い分け方、そして気になるパフォーマンスへの影響と対策方法まで、順番に解説していきます。
Unity C#のリフレクションでできること
リフレクションとは、プログラムが動いている最中に、クラスの型情報を調べたり、メソッドやフィールドを外側から操作したりできる仕組みのことです。
普段のコーディングでは、あらかじめ決まったクラスやメソッドを直接呼び出しますよね。でもリフレクションを使うと、「文字列で指定したクラス名から、実行時にインスタンスを作る」といった、少し変わった動かし方ができるようになります。
具体的には、こんなことが可能になります。
- Unityの内部クラスや、通常は隠れているAPIをスクリプトから操作する
- クラス名を文字列で受け取って、そこから動的にインスタンスを生成する
- 特定の属性(Attribute)が付いたメソッドだけを抽出して、まとめて実行する
たとえば「AnimationウィンドウのRecordボタンを、スクリプトから制御したい」というような、Unityが公式に公開していない機能を使いたい場面で、リフレクションが役立つことがあります。
ただし、これは「知らなくても普段の開発は進められるけれど、知っていると解決できる場面がある」という位置づけの技術です。無理に使う必要はありませんが、選択肢の一つとして持っておくと、ツール開発やエディタ拡張の場面で助けになってくれます。
GetComponentとの違い
「リフレクションって、GetComponentと似たようなものなのかな」と感じる方もいるかもしれません。でも、この二つは仕組みがまったく違います。
GetComponentは、シーン上に配置されたGameObjectから、アタッチされているコンポーネントを取得する仕組みです。あくまでUnityのシーン構造の中での話ですね。
一方でリフレクションは、C#という言語そのものが持つ機能で、クラスの型情報を直接扱います。シーンにオブジェクトが存在しているかどうかは関係なく、クラスの設計図(型情報)そのものにアクセスするイメージです。
属性を組み合わせた活用方法については、こちらの記事でも詳しく紹介しているので、興味があれば読んでみてください。
基本の使い方|Type取得からメソッド実行まで
ここからは、実際にリフレクションを使うときの基本的な流れを、順を追って見ていきましょう。大きく分けると「型を取得する」「インスタンスを作る」「メソッドやフィールドを操作する」という3つのステップになります。
クラスの型(Type)を取得する
まず最初に必要なのが、操作したいクラスの「型情報(Type)」を取得することです。方法は主に2つあります。
- すでにクラス名がわかっている場合は、
typeof(クラス名)を使う - クラス名を文字列としてしか持っていない場合は、
Type.GetType("ネームスペースを含めたフルネーム")を使う
すでに存在がわかっているクラスであれば、基本的にはtypeofを使う方がおすすめです。タイプミスがあればコンパイル時にエラーとして教えてくれますし、処理も少し軽くなります。
注意したいポイント
実行しているコードと取得したいクラスが別のアセンブリにある場合(たとえばエディタ用のコードから、ランタイム側のクラスを取得しようとする場合など)、フルネームだけを渡すとnullが返ってきてしまいます。この場合、エラーが出るわけではなく静かに失敗するので、原因に気づきにくいのが厄介なところです。
その際はType.GetType("ネームスペース.クラス名, Assembly-CSharp")のように、カンマ区切りでアセンブリ名まで指定してあげる必要があります。
インスタンスを生成する
型情報が取得できたら、次はその型のインスタンスを作ります。ここで使うのがActivator.CreateInstance(type)です。
取得したType型を引数として渡すだけで、対応するクラスのインスタンス(object型)を生成してくれます。文字列からクラス名を受け取って、そのままインスタンスを作りたいときにとても便利な方法です。
メソッドを取得して実行する
インスタンスができたら、そのクラスが持つメソッドを呼び出してみましょう。
GetMethod("メソッド名", BindingFlags)で、対象のメソッド情報(MethodInfo)を取得する- もし同じ名前のメソッドが複数ある(オーバーロードされている)場合は、
new Type[] { typeof(int), typeof(string) }のように引数の型を配列で指定して、狙ったメソッドだけを絞り込む - 取得したMethodInfoに対して
Invoke(インスタンス, 引数の配列)を呼び出すと、実際にメソッドが実行される。引数がないメソッドの場合は、引数の配列部分にnullを渡せばOK
ちなみに、メソッド一覧をまとめて取得したいときはGetMethods()、引数の情報を確認したいときはGetParameters()、戻り値の型を知りたいときはReturnTypeが使えます。状況に応じて使い分けてみてください。
フィールド(変数)の値を取得・変更する
最後は、クラスが持っている変数(フィールド)を扱う方法です。
GetField("変数名", BindingFlags)で変数情報(FieldInfo)を取得する。一覧が欲しい場合はGetFields()を使うFieldInfo.GetValue(インスタンス)で、現在の値を取り出すFieldInfo.SetValue(インスタンス, 新しい値)で、値を書き換える
変数の型自体を確認したい場合は、FieldTypeをチェックすれば分かります。

ここまでの流れがつかめると、リフレクションを使った基本的な操作は一通りできるようになります。
BindingFlagsの使い分け方
前のセクションでGetMethodやGetFieldを紹介しましたが、実はそのままではうまく取得できないことがあります。「privateな変数だから取れないのかな」と悩んだ経験がある方もいるのではないでしょうか。
その原因の多くは、BindingFlagsの指定が足りていないことにあります。BindingFlagsは、リフレクションでどの範囲の要素を取得するかを絞り込むための設定です。
主なフラグは、次のように使い分けます。
| フラグ | 意味 |
|---|---|
| Public | public指定の要素を取得する |
| NonPublic | private・protectedなど、public以外の要素を取得する |
| Instance | インスタンスに属する要素を取得する |
| Static | static指定の要素を取得する |
| DeclaredOnly | そのクラス自身で宣言された要素だけを取得する(外すと親クラスの要素も対象になる) |
| FlattenHierarchy | 親クラスのstatic要素も含めて取得する |
たとえば「privateなインスタンス変数を取得したい」場合は、BindingFlags.NonPublic | BindingFlags.Instanceのように組み合わせて指定します。ここを一つでも忘れると、欲しい情報がnullで返ってきてしまうので、思うように取得できないときはまずこの組み合わせを見直してみてください。
見落としやすい注意点
BindingFlags.NonPublicを使えば、本来アクセスできないはずのprivateな変数やメソッドにも触れるようになります。つまり、クラス設計者が「外から触ってほしくない」という意図で隠していた部分を、リフレクションは無視してアクセスできてしまうということです。
「privateだから安全」という前提が崩れる場面があることは、頭の片隅に置いておくとよいかもしれません。
また、親クラスの要素を含めて取得したい場合はDeclaredOnlyを外すという方法もありますが、親クラスのprivate要素まではこの方法では取得できません。

その場合はtype.BaseTypeで対象を親クラス自体に切り替えてから、改めて取得処理を行う必要があります。
メリット・デメリット|使うべきか判断する基準
ここまで基本的な使い方を見てきましたが、「じゃあ実際のプロジェクトで使ってもいいの?」と気になっている方も多いはずです。ここでは、リフレクションのメリットとデメリットを整理しながら、使うべき場面と避けたほうがいい場面を分けて考えてみます。
メリット
リフレクションを使う一番の魅力は、通常のコーディングでは難しい柔軟な処理ができることです。
- Unityの内部クラスや、一般には公開されていないAPIを操作できる
- 文字列からクラスを生成したり、IDとメソッドを動的に紐づけたりと、実装の自由度が広がる
特にエディタ拡張やツール開発の場面では、この柔軟性がそのまま作業効率の向上につながることがあります。
デメリット・注意点
一方で、便利さと引き換えに気をつけたい点もいくつかあります。
- 直接アクセスする場合と比べて、CPU負荷が高く処理速度が遅くなりやすい
- Unityのバージョンアップなどで内部クラスの実装が変わると、依存していたコードが突然動かなくなるリスクがある
- クラス名やメソッド名を文字列で指定するため、タイプミスがそのまま実行時エラーの原因になりやすい
使ってもいい場面・避けたい場面
結論から言うと、エディタ拡張やツール開発での利用は比較的安心して使えますが、ゲームの実行中に毎フレーム呼び出すような処理には向いていません。
エディタ拡張であれば、そもそもゲームのプレイ中に動く処理ではないので、多少の速度差がユーザー体験に影響することはありません。実際、Inspector拡張やEditorWindowを自作するような場面では、リフレクションが活躍する場面がよくあります。
そうしたエディタ拡張の作り方については、こちらの記事でも詳しく解説しているので、あわせて読んでみてください。
逆に、敵の思考ルーチンや移動処理など、毎フレーム呼ばれるような箇所でリフレクションを多用してしまうと、じわじわとパフォーマンスを圧迫してしまうことがあります。こうした場面では、次のセクションで紹介する高速化の工夫を取り入れるか、そもそもリフレクションを使わない設計を検討したほうが安心です。
こうした設計の考え方をもう少し体系的に学んでみたい方には、以下のような書籍も参考になります。
Unityゲーム プログラミング・バイブル 2nd Generation
✅ Amazonでチェックする|✅ 楽天でチェックする
パフォーマンスへの影響と高速化3手法
「エディタ拡張なら大丈夫そうだけど、どうしても実行中の処理でリフレクションを使いたい」という場面も、実はあります。そんなときのために、パフォーマンスへの影響を抑えるための工夫を3つ紹介します。
デリゲートでキャッシュ化する
MethodInfoから直接Invokeを呼び出す方法は手軽ですが、実は引数や戻り値をobject型としてやり取りする際に、型変換(キャスト)や値型のボクシングが発生し、これが処理コストを押し上げる原因になっています。
これを軽くする方法として、Delegate.CreateDelegateを使ってMethodInfoからデリゲートを作成し、そのデリゲートを変数にキャッシュしておくというやり方があります。
一度デリゲート化してしまえば、あとは通常の関数呼び出しとほとんど変わらない速度で実行できるようになります。ポイントは、デリゲートの生成自体は最初の1回だけにして、使い回すことです。毎回生成していては、せっかくの高速化の意味が薄れてしまいます。
デリゲートの基本的な考え方については、こちらの記事でも触れているので、あわせて確認してみるとイメージがつかみやすいかもしれません。
Expression Tree(式木)で高速化する
System.Linq.Expressionsを使うと、ラムダ式を動的に組み立てて、その場でコンパイルし実行することができます。これをリフレクションの代わりに使うことで、速度面の改善が見込めます。
特にフィールドの取得・設定については、FieldInfoから直接デリゲートを作る標準的な方法が用意されていないため、Expression Treeを使ったgetter・setterの生成が有効な手段になります。
注意したいポイント
Compile()によるコンパイル処理自体は決して軽くないので、生成した結果は必ずキャッシュして使い回すようにしてください。また、iOSなどのIL2CPPビルド環境では、動的なコード生成が制限されている場合があり、実機で初めてエラーになるケースもあります。この制限はUnityのバージョンや設定によって変わることがあるため、実機での動作確認は忘れずに行っておくと安心です。
Source Generatorで実行時の処理自体をなくす
ここまでの2つは「リフレクションを速く実行する」工夫でしたが、そもそもリフレクションの実行自体をなくしてしまうというアプローチもあります。それがC#のRoslyn Source Generatorを使う方法です。
Source Generatorは、プログラムの実行時ではなくコンパイルのタイミングで、必要なコードをあらかじめ自動生成してくれる仕組みです。実行時にはリフレクションによる処理そのものが発生しないため、オーバーヘッドを根本からなくすことができます。
こうした仕組みを取り入れているライブラリもあり、実行時のリフレクション処理をなくすことで高速化を図るという設計方針が採用されている例も見られます。DIコンテナの実装を見てみると、こうした工夫が使われていることに気づくことがあるかもしれません。
もう少し踏み込んでC#の実装テクニックを学びたい方には、以下のような書籍も参考になるはずです。
Effective C#
✅ Amazonでチェックする|✅ 楽天でチェックする
まとめ
リフレクションは、型情報をもとにクラスやメソッドを動的に操作できる、少し特殊な技術です。基本的な書き方さえ押さえておけば、Unityの隠れた機能にアクセスしたり、柔軟な設計を実現したりする際の選択肢になってくれます。
大切なのは、エディタ拡張やツール開発では積極的に活用し、実行中の重い処理では慎重に扱うという使い分けです。もしランタイムでどうしても使いたい場面が出てきたら、デリゲートによるキャッシュ化やExpression Treeといった高速化の工夫を思い出してみてください。
まずは小さなツール開発などで、実際に手を動かしながら試してみると、感覚がつかみやすいかもしれません。
よくある質問
-
Qモバイル(iOS/Android)でリフレクションを使っても大丈夫ですか?
-
A基本的な取得・実行(GetMethodやInvokeなど)は動作しますが、Expression Treeなどを使った動的なコード生成については、iOSのIL2CPPビルド環境で制限がかかる場合があります。この挙動はUnityのバージョンや設定によって変わることがあるため、実機でのビルド・動作確認を行ってから使用するのが安心です。
-
QUnityの内部private APIにもリフレクションでアクセスできますか?
-
ABindingFlags.NonPublicを指定すれば、技術的にはアクセスできてしまいます。ただしこれはUnityが公式にサポートしている使い方ではないため、あくまで自己責任での利用になります。バージョンアップで内部実装が変わると突然動かなくなる可能性もあるので、そのリスクを理解した上で使うことをおすすめします。
-
QUnityのバージョンアップでリフレクションのコードが急に動かなくなることはありますか?
-
A起こり得ます。リフレクションはUnityの内部クラスの実装に依存している場合があるため、バージョンアップでその実装が変更されると、これまで動いていたコードが動作しなくなることがあります。特にUnity非公開のAPIに依存している場合は、バージョンアップ後に必ず動作確認を行っておくと安心です。











※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。
※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。