Skip to content

MS_SystemReflectionEmit

nishi_74322014 edited this page Aug 21, 2026 · 1 revision

System.Reflection.Emit

概要

  • Reflection.Emit では、

CIL (Common Intermediate Language: 共通中間言語)

を生成して、メソッド等を作成することができる。

  • アセンブリの IL (Intermediate Language) は、
    • ILSpy
    • .NET Reflector

といったツールを使うことで見ることができる。

補足(位置づけ): System.Reflection.Emit は、
実行時に IL を直接書いて、メソッドや型を作るための API である。
.NET における「動的にコードを作る」手段のうち、最も低水準にあたる。

手段 何を書くか 難易度 速度
Reflection 何も作らない(都度検索) 遅い
式木 式の木Expression 速い
Reflection.Emit IL 命令そのもの 最速
ソース ジェネレーター C# のソース(コンパイル時) 最速 + AOT 可
// 「2 つの int を足す」メソッドを実行時に生成する例
var dm = new DynamicMethod("Add", typeof(int),
                           new[] { typeof(int), typeof(int) });
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);   // 第1引数をスタックへ
il.Emit(OpCodes.Ldarg_1);   // 第2引数をスタックへ
il.Emit(OpCodes.Add);       // 加算
il.Emit(OpCodes.Ret);       // 戻る

var add = (Func<int,int,int>)dm.CreateDelegate(typeof(Func<int,int,int>));
Console.WriteLine(add(2, 3));   // 5

CLR はスタック マシンなので、
「値を積む → 演算する → 結果が積まれる」という流れで記述する。

DynamicMethod は最も手軽な入口で、
アセンブリや型を作らずにメソッド 1 つだけを生成できる。
本格的に型を作るなら AssemblyBuilderModuleBuilder
TypeBuilderMethodBuilder と辿る。

補足(実務で「自分で書く」ことはほぼ無い): 強力だが、

  • IL を間違えると InvalidProgramException で落ちる
    (コンパイラのような検証が無く、原因の特定が難しい)
  • デバッガでステップ実行できない
  • 保守できる人が限られる

という理由で、アプリケーション コードで直接使うことは稀である。

実際には、ライブラリの内部で使われているものを利用する形になる。

ライブラリ Emit の用途
Dapper SQL の結果 → オブジェクトのマッピング
AutoMapper(旧版) プロパティのコピー
Moq / NSubstitute モックの動的生成
Castle DynamicProxy AOP・インターセプター
EF Core(一部) プロキシ、マテリアライザー

**自分で高速化したい場合の第一候補は 式木**である
Expression.Compile() の内部で Emit が使われる)。
式木で書けば、型検査はコンパイラがやってくれるため事故が少なく、
速度も Emit と大差ない
.NET の Reflection の「性能」節を参照)。

補足(トリミング / Native AOT では使えない/最新化): 最も重要な制約である。

実行形態 Reflection.Emit
通常の JIT 実行 利用可能
ReadyToRun 利用可能(JIT が残るため)
Native AOT 利用不可(実行時に IL をコンパイルできない)
Mono AOT(iOS 等) 不可(フル AOT の場合)

Native AOT や iOS 向けでは、
実行時のコード生成という前提そのものが成立しない
このため、Emit / 式木の Compile() に依存するライブラリは
AOT で動かない(あるいはインタープリター動作にフォールバックする)。

現在の推奨は、実行時生成をやめてソース ジェネレーターに移すこと
.NETコンパイラ)。
System.Text.Json や正規表現([GeneratedRegex]
正規表現)が実際にこの移行を行っている。

【従来】 実行時に IL を生成 → 速いが AOT 不可・起動時コストあり
【現在】 コンパイル時に C# を生成 → 速く、AOT 可、デバッグも可能

補足(AssemblyBuilder の保存が復活): .NET Core 以降、
AssemblyBuilder.Save()(生成したアセンブリをディスクに書き出す機能)が
長らく未実装だったが、.NET 9 で PersistedAssemblyBuilder として復活した。
コード生成ツールを .NET で書く場合の選択肢が戻っている。

参考

Qiita

Sho's

(プログラミング C# - 翔ソフトウェア (Sho's))

実装例

Microsoft Learn


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

NetDevInfraWiki

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

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally