C#
AdventCalendar
reflection
リフレクション

タイトルの通り、C#のリフレクションのTIPS集です。

これから示すコードは、以下のusingディレクティブが前提のコードとなってます。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

普段はvarキーワードをよく使ってますが、ここでは変数の型がわかるようにvarキーワードの利用はできるだけ控えています。
それと、いくつかのコードはdynamic使ったほうが簡単に書ける場合もありますが、あくまでもリフレクションのサンプルということでご容赦を。

1. 型名から型情報を得る

Type type = typeof(DateTime);

2. オブジェクトから型情報を得る

var obj = DateTime.Now;
Type type = obj.GetType();

3. 文字列から型情報を得る

var type = Type.GetType("System.DateTime");

4. オブジェクトが指定した型そのものかどうかを調べる

BaseClass obj = new BaseClass();
if (obj.GetType() == typeof(BaseClass))
    Console.WriteLine("BaseClass"); 

もし、objが、BaseClassから派生したクラスのインスタンスだった場合、このコードは、"BaseClass"は出力されません。

5. 型名を得る

Type type = typeof(DateTime);
Console.WriteLine(type.Name);

6. 型のフルネームを得る

Type type = typeof(DateTime);
Console.WriteLine(type.FullName);

7. TypeCodeで型を識別

Action<object> printType = o => {
    TypeCode code = Type.GetTypeCode(o.GetType());
    switch (code) {
        case TypeCode.Boolean:
            Console.WriteLine("bool");
            break;
        case TypeCode.Char:
            Console.WriteLine("char");
            break;
        case TypeCode.Int16:
            Console.WriteLine("short");
            break;
        case TypeCode.Int32:
            Console.WriteLine("int");
            break;
        case TypeCode.Int64:
            Console.WriteLine("long");
            break;
        case TypeCode.Double:
            Console.WriteLine("ddouble");
            break;
        case TypeCode.Decimal:
            Console.WriteLine("decimal");
            break;
        case TypeCode.DateTime:
            Console.WriteLine("DateTime");
            break;
        case TypeCode.String:
            Console.WriteLine("string");
            break;
        case TypeCode.Object:
            Console.WriteLine("object");
            break;
        default:
            Console.WriteLine("other");
            break;
    }
};
printType('c');
printType("aaa");
printType(DateTime.Now);

8. 配列かどうかを調べる

int[] obj = new int[10];
Type type = obj.GetType();
Console.WriteLine(type.IsArray);

9. ジェネリッククラスかどうかを知る

List<string> obj = new List<string>();
Type type = obj.GetType();
Console.WriteLine(type.IsGenericType);

10. 特定のインターフェースを実装しているかを知る

ここでは、IEnumerable<T>を実装しているかを調べている。

List<int> list = new List<int>();
Type type = list.GetType();
bool isGenericIEnumerable = type
                .GetInterfaces()
                .Any(t => t.IsConstructedGenericType &&
                          t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
Console.WriteLine(isGenericIEnumerable);

11. List<T> 型かどうかを調べる

List<int> list = new List<int>();
Type type = list.GetType();
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
    Console.WriteLine("List<T>");

12. ジェネリック型の型引数を知る

Dictionary<int, string> dict = new Dictionary<int, string>();
Type type = dict.GetType();
foreach (Type arg in type.GenericTypeArguments) {
    Console.WriteLine(arg.FullName);
}

これで、intとstringが取り出せます。

13. クラスかどうか調べる

DateTime date = DateTime.Now;
Type type = date.GetType();
Console.WriteLine(type.IsClass);

DateTimeは構造体なので、falseが出力される。

14. 列挙型かを調べる

DayOfWeek obj = DayOfWeek.Sunday;
Type type = obj.GetType();
Console.WriteLine(type.IsEnum);

15. 値に対応する列挙子を求める

DayOfWeek obj = DayOfWeek.Sunday;
Type type = obj.GetType();
string name = type.GetEnumName(1);
Console.WriteLine(name);

16. 列挙子の一覧を得る

DayOfWeek obj = DayOfWeek.Sunday;
Type type = obj.GetType();
foreach (string s in type.GetEnumNames())
    Console.WriteLine(s);

17. 列挙型に定義されている値か?

DayOfWeek obj = DayOfWeek.Sunday;
Type type = obj.GetType();
Console.WriteLine(type.IsEnumDefined(9));
Console.WriteLine(type.IsEnumDefined(6));

18. オブジェクトの名前空間を得る

List<string> obj = new List<string>();
Type type = obj.GetType();
string ns = type.Namespace;
Console.WriteLine(ns);

19. 実行中のコードを含むアセンブリを得る

Assembly assembly = Assembly.GetExecutingAssembly();

20. 実行中のエントリポイントを含むアセンブリを得る

Assembly assembly = Assembly.GetEntryAssembly();

21. アセンブリのフルネームを得る

Assembly assembly = Assembly.GetExecutingAssembly();
Console.WriteLine(assembly.FullName);

22. アセンブリの場所を得る

Assembly assembly = Assembly.GetExecutingAssembly();
Console.WriteLine(assembly.Location);

23. アセンブリのバージョンを取得する

Assembly assembly = Assembly.GetExecutingAssembly();
AssemblyName asmname = assembly.GetName();
Version version = assembly.GetName().Version;
Console.WriteLine(version.ToString());

24. 指定したクラスが属するアセンブリを知る

List<string> obj = new List<string>();
Assembly asm = obj.GetType().Assembly;
Console.WriteLine(asm.FullName);

25. アセンブリに含まれるpublicな型を列挙する

Assembly assembly = obj.GetType().Assembly;
foreach (Type t in assembly.GetTypes().Where(x => x.IsPublic))
    Console.WriteLine(t.FullName);

26. アセンブリ名でアセンブリを動的にロードする

string name = "MyClassLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
AssemblyName assemblyName = new AssemblyName(name);
Assembly assembly = Assembly.Load(assemblyName);                                

27. ファイル名でアセンブリーを動的にロードする

string asmpath = "MyClassLibrary.dll";
Assembly assembly = Assembly.LoadFrom(asmpath);

28. ロードしたアセンブリ内のクラスのインスタンスを生成する

Type myType = assembly.GetType("MyClassLibrary.SampleClass");
object myInstance = Activator.CreateInstance(myType);

29. オブジェクトが定義されているモジュール(dll/exe)名を得る

Type type = this.GetType();
Module modile = type.Module;
Console.WriteLine(module.Name);

30. 引数無しのコンストラクタを呼び出してインスタンスを生成する

あるオブジェクトと同じ型のインスタンスを生成したい時などに利用

Type type = obj.GetType();
ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
object instance = constructor.Invoke(null);
Console.WriteLine(instance.ToString());                                

31. 引数有のコンストラクタを呼ぼ出して、インスタンスを生成する

これを使える場面はあまりないかなー

Type type = obj.GetType();
ConstructorInfo constructor = type.GetConstructor(new Type[] { typeof(int), typeof(string) });
object instance = constructor.Invoke(new object[] { 999, "ZZZ" });

32. オブジェクトがあるクラスから派生されたか知りたい

DerivedClass obj = new DerivedClass();
Type type = obj.GetType();
if (type.IsSubclassOf(typeof(BaseClass)))
    Console.WriteLine("obj is Subclass of BaseClass");

BaseClassから継承しているかを調べている

33. 親クラスを得る

Type type = obj.GetType();
Type parent = type.BaseType;
Console.WriteLine(parent.FullName");

34. 実装しているインターフェースの一覧を得る

List<int> list = new List<int>();
Type type = list.GetType();
Type[] interfaces = type.GetInterfaces();
foreach (Type t in interfaces) {
    Console.WriteLine(t.ToString());
};

35. 指定したインターフェースがもつメソッドを列挙する

Type type = typeof(ICollection<>);
MethodInfo[] methodInfos = type.GetMethods();
foreach (var mi in methodInfos)
    Console.WriteLine(mi.Name);

36. 現在実行されているメソッドが定義されているクラス名を得る

string className = MethodBase.GetCurrentMethod().DeclaringType.FullName;
Console.WriteLine(className);

ちなみに、現在の(インスタンスメソッドの)オブジェクトのクラス名を調べるには、以下のコード。

var type = this.GetType();
Console.WriteLine(type.FullName);

37. 自分自身のメソッド名を得る

string methodName = MethodBase.GetCurrentMethod().Name;
Console.WriteLine(methodName);

38. オブジェクトのインスタンスフィールドを列挙する

Type type = obj.GetType();
FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (FieldInfo m in fields) {
    Console.WriteLine(m.Name);
}

39. オブジェクトのインスタンスプロパティを列挙する

Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (PropertyInfo p in properties) {
    Console.WriteLine(p.Name);
}      

40. オブジェクトのインスタンスメソッドを列挙する

Type type = obj.GetType();
MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public);
foreach (MethodInfo m in methods) {
    Console.WriteLine(m.Name);
}         

41. オブジェクトのクラスで定義されたメソッドだけを取り出す

Type type = obj.GetType();
MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public);
foreach (MethodInfo m in methods.Where(x => x.DeclaringType == obj.GetType())) {
    Console.WriteLine(m.Name);
}

42. 静的メソッドを呼び出す (引数なし)

Type type = obj.GetType();
MethodInfo method = type.GetMethod("Hoge", BindingFlags.Static | BindingFlags.Public);
method.Invoke(null, null);

Hogeメソッドを呼び出す例

43. 静的メソッドを呼び出す (引数あり)

Type type = obj.GetType();
MethodInfo method2 = type.GetMethod("Moge", BindingFlags.Static | BindingFlags.Public);
method2.Invoke(null, new object[] { 5, "ABC" });

44. メソッドの戻り値の型を得る

Type type = obj.GetType();
MethodInfo method = type.GetMethod("Hoge", BindingFlags.Static | BindingFlags.Public);
Type rtype = method.ReturnType;
Console.WriteLine($"rtype.FullName {rtype.FullName}");

ここでは、Hoge静的メソッドの戻り値の型を求めている。

45. インスタンス・メソッドを呼び出す (引数なし)

Type type = obj.GetType();
MethodInfo method = type.GetMethod("Hello");
method.Invoke(obj, null);

46. インスタンス・メソッドを呼び出す (引数あり)

Type type = obj.GetType();
MethodInfo method = type.GetMethod("Hello");
method.Invoke(obj, new[] { "dotnet" });

dynamicにすれば,もっと簡単。でも、コーディング時にメソッド名がわかっていればの話。

dynamic myInstance = obj; 
myInstance.Hello("dotnet");

47. プロパティの値を取得する

Type type = obj.GetType();
PropertyInfo prop = type.GetProperty("MyProperty");
object value = prop.GetValue(obj);
Console.WriteLine(value);   

MyPropertyの値を取り出している

48. プロパティに値を設定する

Type type = obj.GetType();
PropertyInfo prop = type.GetProperty("MyProperty");
prop.SetValue(obj, 987);                                                                              

MyPropertyに987を設定している

49. 書き込み可能なプロパティか

Type type = obj.GetType();
PropertyInfo prop = type.GetProperty("MyProperty");
Console.WriteLine($"書き込み可能か {prop.CanWrite}");

50. 読み込み可能なプロパティか

Type type = obj.GetType();
PropertyInfo prop = type.GetProperty("MyProperty");
Console.WriteLine($"読み込み可能か {prop.CanRead}");

51. プロパティの型を取り出す

Type type = obj.GetType();
PropertyInfo prop = type.GetProperty("MyProperty");
Type propType = prop.PropertyType;
Console.WriteLine(propType.FullName);                                                                          

52. あるプロパティが Nullable<T> 型かどうかを調べたい

Type type = obj.GetType();
PropertyInfo prop = type.GetProperty("MyProperty");
Type propType = prop.PropertyType;
if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(Nullable<>))
    Console.WriteLine("Nullable<T>");

53. クラスに付加された特定の属性を取得する

Type type = obj.GetType();
MyTestAttribute attr = type.GetCustomAttribute<MyTestAttribute>();
Console.WriteLine(attr.MyProperty);

このコードは、MyTestAttribute属性が付加されていることを前提としている。この属性のMyPropertyプロパティの値を表示。

54. プロパティに付加された特定の属性を取得する

Type type = obj.GetType();
PropertyInfo prop = type.GetProperty("StrProperty");
MyTestAttribute attr2 = prop.GetCustomAttribute<MyTestAttribute>();
Console.WriteLine(attr2. MyProperty);

このコードは、MyTestAttribute属性が付加されていることを前提としている。この属性のMyPropertyプロパティの値を表示。

55. 指定した属性が付加されているかを調べる

Type type = obj.GetType();
var isDefined = type.IsDefined(typeof(MyTestAttribute));
Console.WriteLine(isDefined);

PropertyInfo, MethodInfo型のオブジェクトに対しても、IsDefinedメソッドは使えます。