-
Notifications
You must be signed in to change notification settings - Fork 0
MS_SystemReflectionEmit
- Reflection.Emit では、
を生成して、メソッド等を作成することができる。
- アセンブリの IL (Intermediate Language) は、
- ILSpy
- .NET Reflector
といったツールを使うことで見ることができる。
補足(位置づけ):
System.Reflection.Emitは、
実行時に IL を直接書いて、メソッドや型を作るための API である。
.NET における「動的にコードを作る」手段のうち、最も低水準にあたる。
手段 何を書くか 難易度 速度 Reflection 何も作らない(都度検索) 易 遅い 式木 式の木( Expression)中 速い Reflection.EmitIL 命令そのもの 難 最速 ソース ジェネレーター 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)); // 5CLR はスタック マシンなので、
「値を積む → 演算する → 結果が積まれる」という流れで記述する。
DynamicMethodは最も手軽な入口で、
アセンブリや型を作らずにメソッド 1 つだけを生成できる。
本格的に型を作るならAssemblyBuilder→ModuleBuilder→
TypeBuilder→MethodBuilderと辿る。
補足(実務で「自分で書く」ことはほぼ無い): 強力だが、
- 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 で書く場合の選択肢が戻っている。
- ILを使った動的なコード生成入門
- 高速リフレクション-インスタンス生成編
https://qiita.com/yamaokunousausa/items/4061ed39ae9629823e7f - 高速リフレクション-プロパティアクセス編
https://qiita.com/yamaokunousausa/items/9bfa6aa6d7ee593e1e1d
- 高速リフレクション-インスタンス生成編
(プログラミング C# - 翔ソフトウェア (Sho's))
- [C#][.NET] メタプログラミング入門
- Reflection.Emit による Add メソッドの動的生成
http://blog.shos.info/archives/2013/11/csharp_addbyemit.html - 応用編 - オブジェクトの文字列変換のメタプログラミング (Reflection.Emit 編)
http://blog.shos.info/archives/2013/11/csharp_metaprogrammingpraxisemit.html
- Reflection.Emit による Add メソッドの動的生成
- 動的メソッドとアセンブリの生成
https://learn.microsoft.com/ja-jp/dotnet/framework/reflection-and-codedom/emitting-dynamic-methods-and-assemblies - OpCodes クラス (System.Reflection.Emit)
https://learn.microsoft.com/ja-jp/dotnet/api/system.reflection.emit.opcodes - Native AOT 配置の制限事項
https://learn.microsoft.com/ja-jp/dotnet/core/deploying/native-aot/#limitations-of-native-aot-deployment
Tags: 移行, プログラミング, .NET開発
このWikiは「Open棟梁Project」,「OSSコンソーシアム 開発基盤部会」によって運営されています。