C Sharp/リフレクション

出典: フリー教科書『ウィキブックス(Wikibooks)』

リフレクション[編集]

C#におけるリフレクションは、実行時に型やメンバー(プロパティ、メソッド、イベントなど)の情報を取得し、操作するための機能です。リフレクションを使うと、プログラムが実行されている時点で、型の情報を取得したり、動的にインスタンスを生成したり、メソッドやプロパティを呼び出したりできます。

例えば、特定のクラスのメソッドやプロパティの存在、名前、型、引数の情報を取得したり、その情報を使って実行したりすることが可能です。また、リフレクションを使うことで、プログラム実行時に型のインスタンスを生成し、動的にその型のメソッドやプロパティにアクセスすることもできます。

using System;
using System.Reflection;

public class MyClass {
  public int MyProperty {
    get;
    set;
  }
  public void MyMethod() {
    Console.WriteLine("MyMethod() called");
  }
}

class Program {
  static void Main() {
    // クラスの型を取得
    Type myClassType = typeof (MyClass);

    // インスタンスを作成
    object myClassInstance = Activator.CreateInstance(myClassType);

    // プロパティのセットとゲット
    PropertyInfo myPropInfo = myClassType.GetProperty("MyProperty");
    myPropInfo.SetValue(myClassInstance, 42);
    int propValue = (int) myPropInfo.GetValue(myClassInstance);
    Console.WriteLine("MyProperty value: " + propValue); // MyProperty value: 42

    // メソッドの呼び出し
    MethodInfo myMethodInfo = myClassType.GetMethod("MyMethod");
    myMethodInfo.Invoke(myClassInstance, null); // MyMethod() called
  }
}

この例では、MyClassというクラスが定義されています。Mainメソッドでは、MyClassの型情報を取得し、リフレクションを使ってそのクラスのインスタンスを作成します。その後、GetPropertySetValueGetValueなどのリフレクションメソッドを使用して、プロパティの値を設定したり取得したりし、GetMethodInvokeを使ってメソッドを呼び出しています。

このように、リフレクションを使うことで、実行時に動的にクラスやそのメンバーにアクセスできます。ただし、この方法はコンパイル時の型チェックを回避するため、適切なエラーハンドリングや型の確認が重要です。

メソッド一覧を得る[編集]

特定のクラスのメソッド一覧を取得するためには、リフレクションを使用します。以下は、特定のクラスのメソッド一覧を取得するC#の例です。

using System;
using System.Reflection;

public class MyClass {
  public void Method1() {}
  public int Method2(int value) {
    return value * 2;
  }
  public string Method3(string input) {
    return "Hello, " + input;
  }
}

class Program {
  static void Main() {
    Type myClassType = typeof (MyClass);

    // クラスのすべてのメソッドを取得
    MethodInfo[] methods = myClassType.GetMethods();

    Console.WriteLine("Methods in MyClass:");
    foreach(var method in methods) {
      Console.WriteLine("Method Name: " + method.Name);

      // メソッドの戻り値の型
      Console.WriteLine("Return Type: " + method.ReturnType.Name);

      // メソッドの引数
      ParameterInfo[] parameters = method.GetParameters();
      Console.WriteLine("Parameters:");
      foreach(var param in parameters) {
        Console.WriteLine($" - Name: {param.Name}, Type: {param.ParameterType.Name}");
      }

      Console.WriteLine();
    }
  }
}
実行結果
Methods in MyClass:
Method Name: Method1
Return Type: Void
Parameters:

Method Name: Method2
Return Type: Int32
Parameters:
 - Name: value, Type: Int32

Method Name: Method3
Return Type: String
Parameters:
 - Name: input, Type: String

Method Name: GetType
Return Type: Type
Parameters:

Method Name: ToString
Return Type: String
Parameters:

Method Name: Equals
Return Type: Boolean
Parameters:
 - Name: obj, Type: Object

Method Name: GetHashCode
Return Type: Int32
Parameters:

このコードは、C#のリフレクションを使用して特定のクラス(MyClass)のメソッド情報を取得し、それらの情報をコンソールに出力するものです。

  1. MyClassというクラスが定義されています。このクラスには3つのメソッドが含まれています。
  2. ProgramクラスのMainメソッドでは、MyClassの型情報を取得しています。これにより、リフレクションを使ってMyClassのメソッドにアクセスできるようになります。
  3. GetMethods()メソッドを使用して、MyClassに含まれるすべてのメソッドの情報を取得しています。これにより、MethodInfo型の配列methodsにメソッドの情報が格納されます。
  4. foreachループを使用して、各メソッドの情報を取り出し、コンソールに出力しています。
    • method.Nameでメソッド名を取得し、Console.WriteLineを使って表示しています。
    • method.ReturnType.Nameで戻り値の型情報を取得し、それも表示しています。
    • method.GetParameters()を使ってメソッドの引数情報を取得し、ParameterInfo型の配列parametersに格納しています。
    • 引数の情報(名前と型)をforeachループを使って取り出し、コンソールに出力しています。

using System;
using System.Reflection;

class Program {
  static void Main() {
    // クラス名を指定して Type オブジェクトを取得
    Type type = typeof (int);

    // クラス名を出力
    Console.WriteLine($"クラス名:{type.Name} {{");

    // フィールドの情報を取得して出力
    Console.WriteLine("\t// フィールド:");
    FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
    foreach(var field in fields) {
      Console.WriteLine($"\t{field.FieldType.Name} {field.Name};");
    }
    Console.WriteLine();

    // メソッドの情報を取得して出力
    Console.WriteLine("\t//メソッド:");
    MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
    foreach(var method in methods) {
      Console.Write($"\t{method.ReturnType.Name} {method.Name}(");

      // メソッドの引数を出力
      ParameterInfo[] parameters = method.GetParameters();
      for (int i = 0; i < parameters.Length; i++) {
        Console.Write($"{parameters[i].Name} {parameters[i].ParameterType.Name}");
        if (i < parameters.Length - 1) {
          Console.Write(", ");
        }
      }

      Console.WriteLine(");");
    }

    Console.WriteLine("}");
  }
}
実行結果
クラス名:Int32 {
    // フィールド:
    Int32 m_value;

    //メソッド:
    Int32 CompareTo(value Object);
    Int32 CompareTo(value Int32);
    Boolean Equals(obj Object);
    Boolean Equals(obj Int32);
    Int32 GetHashCode();
    String ToString();
    String ToString(format String);
    String ToString(provider IFormatProvider);
    String ToString(format String, provider IFormatProvider);
    Boolean TryFormat(destination Span`1, charsWritten Int32&, format ReadOnlySpan`1, provider IFormatProvider);
    Boolean TryFormat(utf8Destination Span`1, bytesWritten Int32&, format ReadOnlySpan`1, provider IFormatProvider);
    TypeCode GetTypeCode();
    Boolean System.IConvertible.ToBoolean(provider IFormatProvider);
    Char System.IConvertible.ToChar(provider IFormatProvider);
    SByte System.IConvertible.ToSByte(provider IFormatProvider);
    Byte System.IConvertible.ToByte(provider IFormatProvider);
    Int16 System.IConvertible.ToInt16(provider IFormatProvider);
    UInt16 System.IConvertible.ToUInt16(provider IFormatProvider);
    Int32 System.IConvertible.ToInt32(provider IFormatProvider);
    UInt32 System.IConvertible.ToUInt32(provider IFormatProvider);
    Int64 System.IConvertible.ToInt64(provider IFormatProvider);
    UInt64 System.IConvertible.ToUInt64(provider IFormatProvider);
    Single System.IConvertible.ToSingle(provider IFormatProvider);
    Double System.IConvertible.ToDouble(provider IFormatProvider);
    Decimal System.IConvertible.ToDecimal(provider IFormatProvider);
    DateTime System.IConvertible.ToDateTime(provider IFormatProvider);
    Object System.IConvertible.ToType(type Type, provider IFormatProvider);
    Int32 System.Numerics.IBinaryInteger<System.Int32>.GetShortestBitLength();
    Int32 System.Numerics.IBinaryInteger<System.Int32>.GetByteCount();
    Boolean System.Numerics.IBinaryInteger<System.Int32>.TryWriteBigEndian(destination Span`1, bytesWritten Int32&);
    Boolean System.Numerics.IBinaryInteger<System.Int32>.TryWriteLittleEndian(destination Span`1, bytesWritten Int32&);
    Type GetType();
    Object MemberwiseClone();
    Void Finalize();

}

このコードは、C#のリフレクションを使用して、与えられたクラスの情報を取得し、そのフィールドとメソッドをコンソールに出力するものです。以下、コードの解説です。

  1. using System;using System.Reflection; は、必要な名前空間をインポートしています。
  2. static void Main() メソッドはプログラムのエントリーポイントです。ここでクラスの情報を取得し、出力を行います。
  3. Type type = typeof(int); の部分で、typeof キーワードを使用して、指定した型 (int の場合) の Type オブジェクトを取得しています。この部分を、調査したいクラスの型に置き換えることで、そのクラスの情報を取得できます。
  4. Console.WriteLine($"クラス名:{type.Name} {{"); は、取得したクラスの名前をコンソールに出力しています。ここでクラスの開始を示す { も表示しています。
  5. FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); は、指定したクラスのフィールド情報を取得しています。BindingFlags は、取得するフィールドの種類を指定しています。
  6. foreach(var field in fields) ループでは、取得したフィールドの情報を一つずつ取り出して、その型と名前をコンソールに出力しています。
  7. 同様に、メソッドの情報を取得しています。MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); でメソッド情報を取得し、それぞれのメソッドの戻り値の型と名前、引数の情報をコンソールに出力しています。
  8. 最後に、} でクラスの終了を示しています。

あるクラスからObjectまで親クラスを遡る[編集]

C#で特定のクラスから親クラスを Object まで遡るコードをリフレクションを使って書くことができます。以下はその例です。

using System;

class Program {
  static void Main() {
    Type type = typeof (int);

    Console.WriteLine($"クラス: {type.Name}");

    Type currentType = type;

    // 親クラスが Object になるまでループで親クラスを表示
    while (currentType.BaseType != null) {
      Console.WriteLine($"親クラス: {currentType.BaseType.Name}");
      currentType = currentType.BaseType;
    }
  }
}
実行結果
クラス: Int32
親クラス: ValueType
親クラス: Object

このコードでは、int の親クラスを Object まで遡って表示します。

typeof 演算子は、指定された型の Type オブジェクトを取得します。BaseType プロパティは、その型の直接の親クラスを返します。このプロパティを使って親クラスを取得し、Object まで遡るまでループを繰り返し、各親クラスの名前をコンソールに出力します。