Skip to content

MS_DotNetReflection

nishi_74322014 edited this page Aug 21, 2026 · 1 revision

.NET の Reflection

概要

System.Type と System.Reflection 名前空間のクラスと共に使用する。

補足(リフレクションとは何か): .NET のアセンブリは
自己記述的.NET のアセンブリ)で、
「どんな型があり、どんなメンバーを持つか」というメタデータ
自分自身の中に持っている。

リフレクションは、このメタデータを実行時に読み取り、操作する機能である。

【通常のコード】
   var o = new Foo();      ← コンパイル時に Foo が決まる
   o.Bar();

【リフレクション】
   Type t = Type.GetType("Foo");           ← 文字列で型を指定
   object o = Activator.CreateInstance(t);
   t.GetMethod("Bar").Invoke(o, null);     ← 文字列でメソッドを指定

型名やメソッド名が実行時にしか分からない」場面
(プラグイン、DI コンテナー、O/R マッパー、シリアライザー)で
不可欠になる。実際、ASP.NET Core の DI や
ADO.NET / ORM の内部でも使われている。

詳細

使用例

リフレクションを使用すると、以下のような処理を実装できる。

以下の情報を取得できる。

  • 読み込まれたアセンブリについての情報、
  • およびそのアセンブリ内に定義されている
    クラス、インターフェイス、値型などの型。

型インスタンス

  • 実行時に型インスタンスを作成
  • 作成した型インスタンスを呼出

参考

補足(非パブリック メンバーへのアクセス): 上記の
「隠蔽されている非パブリックメンバを呼び出す」は
BindingFlags.NonPublic を使う技法だが、
本番コードでの常用は避けるべきである。

  • ライブラリの内部実装は予告なく変わる(更新で静かに壊れる)
  • 部分信頼環境やサンドボックスではセキュリティ例外になり得る
  • トリミング / Native AOT で削除される可能性がある

テスト コードや調査目的に限定するのが妥当。
恒常的に必要なら InternalsVisibleTo 属性を検討する。

性能

System.Reflection.Emit

静的なコードの数倍程度までは速くできる。

  • 静的なコードの数倍程度までは速くできる。
  • System.Reflection.Emit するものと比べてそう大きな差はない。

補足(なぜ遅いのか): リフレクションが遅い理由は、
主に次の 3 つに分解できる。

要因 内容
メンバー検索 GetMethod("Bar") は名前で文字列比較しながら探す
引数の詰め替え 引数を object[] にする → ボックス化とアロケーション
呼び出し経路 JIT の最適化(インライン化等)が効かない

原文が言う「アクセッサのキャッシュ」とは、
①(検索)を 1 回だけにして、結果をデリゲートとして保持する手法である。

// 遅い:呼ぶたびに検索+ボックス化
var v = type.GetProperty("Name").GetValue(obj);

// 速い:初回だけコンパイルし、以降はデリゲート呼び出し
Func<Foo, string> getter = _cache.GetOrAdd(key, k => {
    var p = Expression.Parameter(typeof(Foo));
    return Expression.Lambda<Func<Foo, string>>(
        Expression.Property(p, "Name"), p).Compile();
});
var v2 = getter(foo);

補足(現在の追加の選択肢):

手段 備考
ソース ジェネレーター コンパイル時に生成。実行時コストがゼロで AOT 対応
MethodInfo.CreateDelegate 引数の型が既知なら式木より手軽で速い
System.Text.Json のソース生成 シリアライザーの実例

現在の第一候補はソース ジェネレーターである。
リフレクション/Emit/式木はいずれも実行時のコード生成であり、
後述のトリミング・AOT と本質的に相性が悪いのに対し、
ソース ジェネレーターはその問題を回避できる
.NETコンパイラ)。

互換性

補足(.NET Core 以降の状況/最新化): 原文執筆時点で「不明」
とされていた点について、現在は次の通り整理されている。

状況
Reflection 全般 .NET Core / .NET で利用可能。API もほぼ揃った
式木(Compile() 利用可能。ただし内部で Reflection.Emit を使う
Reflection.Emit 利用可能AssemblyBuilder の永続化保存は .NET 9 で復活

ただし、制約が生じるのは実行形態の側である。

【通常の JIT 実行】       すべて使える

【トリミング(Trimming)】 参照されていない型が削除される
                            → 文字列で引く型が消えていることがある

【Native AOT】             実行時のコード生成が原則できない
                            → Reflection.Emit / Expression.Compile が不可
                            → リフレクションも制限付き

このため、トリミング / Native AOT を使う場合は、

  • [DynamicDependency] / [DynamicallyAccessedMembers] 属性で
    トリマーに「消すな」と伝える、
  • あるいはソース ジェネレーターに置き換える

という対応が必要になる
.NET Coreの配置)。

参考

実装

性能

互換性

Microsoft Learn


Tags: 移行, プログラミング, .NET開発

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally