C#リフレクション:実行時型情報取得と動的プログラミング
リフレクション(Reflection)オブジェクトは、実行時に型情報を取得するために使用されます。実行中のプログラムのメタデータにアクセスするクラスはSystem.Reflection名前空間にあります。
System.Reflection名前空間には、アプリケーションについての情報を取得し、型、値、オブジェクトを動的にアプリケーションに追加するクラスが含まれています。
リフレクションの用途
- 実行時に属性情報を表示
- アセンブリ内のさまざまな型を調べ、それらをインスタンス化
- メソッドとプロパティへの遅延バインディング
- 実行時に新しい型を作成し、それらを使用してタスクを実行
メタデータの表示
前章で説明したように、リフレクションを使用して属性情報を表示できます。
System.ReflectionクラスのMemberInfoオブジェクトを初期化して、クラスに関連付けられた属性を検出する必要があります。これを行うには、対象クラスのオブジェクトを定義します。
System.Reflection.MemberInfo info = typeof(MyClass);
属性表示の例
using System;
[AttributeUsage(AttributeTargets.All)]
public class HelpAttribute : System.Attribute {
public readonly string Url;
public string Topic {
get => topic;
set => topic = value;
}
public HelpAttribute(string url) {
this.Url = url;
}
private string topic;
}
[HelpAttribute("Information on the class MyClass")]
class MyClass { }
namespace AttributeAppl {
class Program {
static void Main(string[] args) {
System.Reflection.MemberInfo info = typeof(MyClass);
object[] attributes = info.GetCustomAttributes(true);
for (int i = 0; i < attributes.Length; i++) {
System.Console.WriteLine(attributes[i]);
}
Console.ReadKey();
}
}
}
実行結果:
HelpAttribute
実践例:DeBugInfo属性の読み取り
前章で作成したDeBugInfo属性を使用し、リフレクションでRectangleクラスのメタデータを読み取ります。
using System;
using System.Reflection;
namespace BugFixApplication {
[AttributeUsage(
AttributeTargets.Class
| AttributeTargets.Constructor
| AttributeTargets.Field
| AttributeTargets.Method
| AttributeTargets.Property,
AllowMultiple = true
)]
public class DeBugInfo : System.Attribute {
private int bugNo;
private string developer;
private string lastReview;
public string message;
public DeBugInfo(int bg, string dev, string d) {
bugNo = bg; developer = dev; lastReview = d;
}
public int BugNo => bugNo;
public string Developer => developer;
public string LastReview => lastReview;
public string Message { get => message; set => message = value; }
}
[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle {
protected double length;
protected double width;
public Rectangle(double l, double w) {
length = l; width = w;
}
[DeBugInfo(55, "Zara Ali", "19/10/2012", Message = "Return type mismatch")]
public double GetArea() => length * width;
[DeBugInfo(56, "Zara Ali", "19/10/2012")]
public void Display() {
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
class ExecuteRectangle {
static void Main(string[] args) {
Rectangle r = new Rectangle(4.5, 7.5);
r.Display();
Type type = typeof(Rectangle);
// クラスレベルの属性を取得
foreach (object attr in type.GetCustomAttributes(false)) {
if (attr is DeBugInfo dbi) {
Console.WriteLine("Bug No: {0}", dbi.BugNo);
Console.WriteLine("Developer: {0}", dbi.Developer);
Console.WriteLine("Last Reviewed: {0}", dbi.LastReview);
Console.WriteLine("Remarks: {0}", dbi.Message);
}
}
// メソッドレベルの属性を取得
foreach (MethodInfo m in type.GetMethods()) {
foreach (Attribute a in m.GetCustomAttributes(true)) {
if (a is DeBugInfo dbi) {
Console.WriteLine("Bug No: {0}, for Method: {1}", dbi.BugNo, m.Name);
Console.WriteLine("Developer: {0}", dbi.Developer);
Console.WriteLine("Last Reviewed: {0}", dbi.LastReview);
Console.WriteLine("Remarks: {0}", dbi.Message);
}
}
}
Console.ReadLine();
}
}
}
実行結果:
Length: 4.5
Width: 7.5
Area: 33.75
Bug No: 49
Developer: Nuha Ali
Last Reviewed: 10/10/2012
Remarks: Unused variable
Bug No: 45
Developer: Zara Ali
Last Reviewed: 12/8/2012
Remarks: Return type mismatch
Bug No: 55, for Method: GetArea
Developer: Zara Ali
Last Reviewed: 19/10/2012
Remarks: Return type mismatch
Bug No: 56, for Method: Display
Developer: Zara Ali
Last Reviewed: 19/10/2012
Remarks:
リフレクションの主要API
Typeクラス(型情報のエントリポイント)
| プロパティ/メソッド | 説明 |
|---|---|
typeof(T) / obj.GetType() | Typeオブジェクトを取得 |
Name / FullName / Namespace | 型名情報 |
Assembly | 所属アセンブリ |
BaseType | 基底型 |
GetInterfaces() | 実装インターフェース配列 |
IsClass/IsInterface/IsValueType/IsAbstract/IsSealed | 型種別判定 |
GetMembers() | すべてのメンバー取得 |
GetMethods()/GetProperties()/GetFields()/GetConstructors() | 各種メンバー取得 |
GetCustomAttributes() | 属性取得 |
CreateInstance() | インスタンス生成(遅延バインディング) |
動的インスタンス生成とメソッド呼び出し
Type type = typeof(MyClass);
object instance = Activator.CreateInstance(type); // デフォルトコンストラクタ
// パラメータ付きコンストラクタ
object instance2 = Activator.CreateInstance(type, arg1, arg2);
// メソッド呼び出し
MethodInfo method = type.GetMethod("MyMethod");
object result = method.Invoke(instance, new object[] { arg1, arg2 });
// プロパティ設定/取得
PropertyInfo prop = type.GetProperty("MyProperty");
prop.SetValue(instance, "New Value");
object value = prop.GetValue(instance);
アセンブリ操作
// 現在のアセンブリ
Assembly asm = Assembly.GetExecutingAssembly();
// ファイルからロード
Assembly asm2 = Assembly.LoadFrom("MyLibrary.dll");
// アセンブリ内のすべての型を取得
Type[] types = asm.GetTypes();
// 特定の型を取得
Type t = asm.GetType("MyNamespace.MyClass");
式木(Expression Trees)と動的コード生成
リフレクションのさらに高度な用途として、実行時にコードを生成・コンパイル・実行できます。
using System.Linq.Expressions;
// x => x + 1 というラムダ式を式木として構築
ParameterExpression x = Expression.Parameter(typeof(int), "x");
Expression> lambda = Expression.Lambda>(
Expression.Add(x, Expression.Constant(1)), x
);
// コンパイルしてデリゲートとして実行
Func compiled = lambda.Compile();
int result = compiled(5); // 6
パフォーマンス考慮事項
- リフレクションは通常の直接呼び出しより遅い(キャッシュで緩和可能)
Type.GetType()、GetMethods()等の結果をキャッシュする- 頻繁に呼び出す動的メソッド呼び出しには
Delegate.CreateDelegateや式木コンパイルを検討 Activator.CreateInstanceよりコンパイル済みデリゲートやFunc<T>ファクトリの方が高速
よくあるユースケース
- シリアライザ/デシリアライザ(JSON、XML、Binary)
- DIコンテナ(依存性注入フレームワーク)
- ORM(Entity Framework、Dapper等)
- テストフレームワーク(属性ベースのテスト検出)
- プラグインアーキテクチャ(外部アセンブリからの型検出・インスタンス化)
- マッピングライブラリ
- 動的プロキシ生成
ベストプラクティス
- リフレクションは「最後の手段」として使用し、可能ならジェネリクス/インターフェース/デリゲートで静的に解決する
BindingFlagsを適切に指定して検索範囲を絞る(Public | Instance等)- nullチェックを怠らない(
GetMethodは見つからないとnullを返す) - 例外処理を適切に行う(
TargetInvocationException等のアンラップ) - セキュリティ:
ReflectionPermission、信頼されていないコードからのリフレクション制限に注意
C言語
- C# Hello World – 初めてのC#アプリケーション構築
- C# キーワードと識別子:ルール、一覧、ベストプラクティス完全ガイド
- C# 変数とプリミティブデータ型:完全マスターガイド
- C#演算子完全ガイド:C#における演算子の包括的解説
- C# ビット演算子とビットシフト演算子をマスターする:包括的ガイド
- C# 入出力の基礎:ユーザー入力と出力の基本をマスター
- C# の式、文、ブロック:実践例で徹底理解
- C# コメントをマスターする:種類、ベストプラクティス、XML ドキュメント
- C# break 文:使い方とタイミングを徹底解説
- C# continue 文をマスターする:ループ反復を効果的にスキップする方法
- C# 配列をマスターする:宣言、初期化、操作