概要
リフレクションってのは、我々が慣れ親しんだ方法とは違う方法……リフレクション API を使って……クラスだとかメソッドだとかにアクセスする機能のことだ。へー。流し読むぞ。
流し読みノート
めたでーた
- アセンブリには、型のメンバや実装の詳細が含まれたメタデータが入っている。
- そのメタデータのおかげで CLR はコンパイルやガベージコレクションができるってわけよ。
- メタデータは CLR だけが使えるわけじゃない。ぼくらも「リフレクション API」(
System.Reflection
名前空間のクラス)を使って型の情報にアクセスできるのだ。
リフレクション API ってどんなの
こんなプロパティや型の階層構造になってる。
Assembly
└── Module
└── TypeInfo
├── FieldInfo
├── MethodInfo
├── ConstructorInfo
├── PropertyInfo
└── EventInfo
などなど。これらは双方向にたどれる。
Assembly
プロパティとかModule
はみたまんま。アセンブリやモジュールの情報をもつ。Module
はあんま利用しない。だってアセンブリの大部分はひとつのモジュールから構成されているから。TypeInfo
クラスはもちろん型を表す。これ以下はぜんぶMemberInfo
を継承してる。ぜんぶなにかの型のメンバだから。FieldInfo
はTypeInfo
じゃなくてType
オブジェクトから取得するのが一般的。- いや、なんで
TypeInfo
とType
があるねんややこしすぎるわ。 - まあ使い分けとしては、
Type
は型を同定したり基本的な情報を知りたいとき使う。Type
は軽量クラスとも表現されてるみたい。リフレクションが必要なときはTypeInfo
を使う。ジェネリック型のサポートによりこれらの役割はさらにわかりにくくなった。メソッドにはこういう2種類の表現はない。型だけが2種類もってる。
- いや、なんで
PropertyInfo
はTypeInfo
オブジェクトのGetDeclaredProperty()
とかDeclaredProperties
プロパティで取得。こいつはアクセシビリティに関するプロパティがない。だってアクセシビリティは個々のget
set
メソッドのレベルに設定されているからね。プロパティの型はPropertyType
プロパティから取得。EventInfo
↑と同じようにGetDeclareEvent()
やDeclaredEvent
で取得できて、アクセシビリティも個々のadd
remove
にあるので本体はアクセシビリティに関するプロパティはもたない。add
remove
についてはGetAddMethod
GetRemoveMethod
でMethodInfo
として取得できる。
Assembly プロパティのうんちく
Assembly.GetEntryAssembly
、Assembly.GetExecutingAssembly
、Assembly.GetCallingAssembly
あたりでAssembly
オブジェクトを取得できる。- ただ現在のアセンブリがほしいときは
Assembly.GetExecutingAssembly
よりtypeof(Program).GetTypeInfo().Assembly
のほうが速い。 Assembly
クラスのDefinedTypes
プロパティでアセンブリ内のすべての型を取得できる。ExportedTypes
プロパティならpublic
な型だけ絞って取得。- てかなんならこの
Assembly
プロパティから「型のインスタンス」を生成できる。
object o = asm.CreateInstance(
"MyApp.WithConstructor",
false, // 名前のマッチを厳密に(大文字小文字を区別して)行う。
BindingFlags.Public | BindingFlags.Instance, // public なインスタンスコンストラクタを使う。
null, // Binder オブジェクトを指定しないことにより、与える引数が正確に型マッチすることを期待する。
new object[] { "コンストラクタの引数" },
null, // CultureInfo を渡すところ。
null); // Remote activation のようなまれなケースに対応するための引数。(しらん)
- ↑の
BindingFlags
について。リフレクション API の多くはこれを引数にとる。どのメンバを取得するか選ぶときに使うんだって。BindingFlags.Public
とかBindingFlags.NonPublic
とかを見ればわかりやすいね。 - えーと? クラス自体を作れるんじゃなくて、実在する
WithConstructor
クラスのインスタンスを作っている……のか? 「型のインスタンス」って言い方わかりづらい。
Type と TypeInfo のうんちく
// Type の取得。
Type t1 = typeof(string);
Type t2 = typeof(IDisposable);
// TypeInfo を使って、型互換性のテストをしてみる。
// string -> object にできるけど、逆はできない。
TypeInfo stringType = typeof(string).GetTypeInfo();
TypeInfo objectType = typeof(object).GetTypeInfo();
Console.WriteLine(objectType.IsAssignableFrom(stringType)); // True。 string -> object 可。
Console.WriteLine(stringType.IsAssignableFrom(objectType)); // False。 object -> string 不可。
ConstructorInfo
とMethodInfo
はどっちもメソッドなんでMethodBase
から派生している。- アクセシビリティに対応して
IsPublic
(public
)、IsPrivate
(private
)、IsAssembly
(internal
)、IsFamily
(protected
)、IsFamilyOrAssembly
(protected internal
)なんつープロパティがある。protected
って「ファミリー」なんだ。へー。- あと
IsFamilyAndAssembly
も……たぶん CTS(Common Type System)に……あるんだけど、これは C# には無い。
- あと
冒頭の「リフレクション API でメソッドにアクセスする」実例
// InvokeMember を使ってメソッドを呼び出す。
public static object CreateAndInvokeMethod(string typeName, string member, params object[] args)
{
Type t = Type.GetType(typeName);
object instance = Activator.CreateInstance(t);
return t.InvokeMember(
member,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod,
null,
instance,
args);
}
// MemberInfo を使ってメソッドを呼び出す。
public static object CreateAndInvokeMethod(string typeName, string member, params object[] args)
{
Type t = Type.GetType(typeName);
object instance = Activator.CreateInstance(t);
MethodInfo m = t.GetTypeInfo().DeclaredMethods.Single(mi => mi.Name == member);
return m.Invoke(instance, args);
}
小休憩
- .NET 4.5 から追加されたという「リフレクションコンテキスト」は需要がよくわからん。スキップ。
- リフレクション API を使うと「利用する型の構造に基づいて動作するコード」を作れるという。よく需要がよくわからん。
必要性を実感できないものは理解しづらい。ユニットテストとか書くときに使えるかな? くらい。