-
Notifications
You must be signed in to change notification settings - Fork 0
MS_DotNetReflection
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 の内部でも使われている。
リフレクションを使用すると、以下のような処理を実装できる。
- 読み込まれたアセンブリについての情報、
- およびそのアセンブリ内に定義されている
クラス、インターフェイス、値型などの型。
- 実行時に型インスタンスを作成
- 作成した型インスタンスを呼出
- .NET Tips (VB.NET,C#...)
- 型(クラス、構造体など)のすべてのメンバを取得する
https://dobon.net/vb/dotnet/programing/typegetmembers.html - 型に指定した名前のメンバがあるか調べる
https://dobon.net/vb/dotnet/programing/typegetmember.html - 型のメンバを動的に呼び出す
https://dobon.net/vb/dotnet/programing/typeinvokemember.html - 隠蔽されている非パブリックメンバを呼び出す
https://dobon.net/vb/dotnet/programing/invokenonpublicmember.html - フォームに配置されているコントロールを名前で探す
https://dobon.net/vb/dotnet/control/findcontrolbyname.html - 画像やテキストファイルを実行ファイルに埋め込む
https://dobon.net/vb/dotnet/programing/bitmapresource.html
- 型(クラス、構造体など)のすべてのメンバを取得する
補足(非パブリック メンバーへのアクセス): 上記の
「隠蔽されている非パブリックメンバを呼び出す」は
BindingFlags.NonPublicを使う技法だが、
本番コードでの常用は避けるべきである。
- ライブラリの内部実装は予告なく変わる(更新で静かに壊れる)
- 部分信頼環境やサンドボックスではセキュリティ例外になり得る
- トリミング / Native AOT で削除される可能性がある
テスト コードや調査目的に限定するのが妥当。
恒常的に必要ならInternalsVisibleTo属性を検討する。
-
一般的に遅いと言われているが、以下で実用的な速度までチューニング可能。
-
以下で生成したアクセッサのキャッシュが実用的。
静的なコードの数倍程度までは速くできる。
- 静的なコードの数倍程度までは速くできる。
- 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コンパイラ)。
- コチラを見ると、互換性ありそう。
- ただし、System.Reflection.Emit、式木(Expression Tree)については、不明。
補足(.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の配置)。
- 実行時型情報 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C
https://ufcpp.net/study/csharp/sp_reflection.html - C#リフレクションTIPS 55連発 - Qiita
https://qiita.com/gushwell/items/91436bd1871586f6e663
-
なぜリフレクションは遅いのか | POSTD
https://postd.cc/why-is-reflection-slow/ -
[雑記] 動的コード生成のパフォーマンス - C# によるプログラミング入門 | ++C++; // 未確認飛行 C
https://ufcpp.net/study/csharp/misc_dynamic.html -
Reflection vs. compiled expressions vs. delegates - Performance comparison - www.palmmedia.de\ http://www.palmmedia.de/blog/2012/2/4/reflection-vs-compiled-expressions-vs-delegates-performance-comparison
- c# - Using Reflection in .NET Core - Stack Overflow
https://stackoverflow.com/questions/36118978/using-reflection-in-net-core - c# – .NET CoreでReflectionを使用する - コードログ
https://codeday.me/jp/qa/20190121/184182.html
- リフレクション (C#)
https://learn.microsoft.com/ja-jp/dotnet/csharp/advanced-topics/reflection-and-attributes/ - リフレクション (Visual Basic)
https://learn.microsoft.com/ja-jp/dotnet/visual-basic/programming-guide/concepts/reflection - トリミングに関する既知の問題(リフレクション)
https://learn.microsoft.com/ja-jp/dotnet/core/deploying/trimming/incompatibilities
Tags: 移行, プログラミング, .NET開発
このWikiは「Open棟梁Project」,「OSSコンソーシアム 開発基盤部会」によって運営されています。