reflectionという英単語には「反射」や「反響」といった意味、また「内省」や「熟考」などの自分自身を見つめるといった意味があります。 「自己言及」と訳される場合もあります。 プログラミングにおけるリフレクションとは、プログラムのメタデータ(=型情報などプログラム自身に埋め込まれている情報)を実行時に取得することを指します。

.NETではプログラム(厳密にはコードのコンパイルによって生成されるモジュール)に様々なメタデータが埋め込まれます。 リフレクションによって埋め込まれたメタデータを参照することによって、型情報や属性の取得、型情報のみによるインスタンスの作成、遅延バインディング、アセンブリの動的読み込み・生成など、さまざまなことが行えるようになっています。 文字列で名前を指定してメソッドを呼び出したり、文字列で型を選択するといった手法もリフレクションによって実装することができます。

リフレクションの例

型情報の取得

リフレクションによって実行時に型自身の情報を取得することができます。 取得できる情報については後述の§.Typeクラスから取得できる情報§.メンバ情報の取得 (MemberInfo)などで解説します。

リフレクション機能を使って実行時に型情報を取得する
using System;
using System.Reflection;

class Account {
  public int ID { get; set; }
  public string Name { get; set; }

  public Account(int id, string name)
  {
    this.ID = id;
    this.Name = name;
  }
}

class Sample {
  static void Main()
  {
    // Accountクラスの型情報を取得する
    var t = typeof(Account);

    Console.WriteLine("Name = {0}", t.Name);
    Console.WriteLine("BaseType = {0}", t.BaseType);

    // Accountクラスのすべてのメンバ情報を取得する
    // (パブリックおよび非パブリックのインスタンスメンバを取得する)
    var members = t.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

    foreach (var m in members) {
      Console.WriteLine("MemberType = {0}, Name = {1}", m.MemberType, m.Name);
    }
  }
}
リフレクション機能を使って実行時に型情報を取得する
Imports System
Imports System.Reflection

Class Account
  Public Property ID As Integer
  Public Property Name As String

  Public Sub New(ByVal id As Integer, ByVal name As String)
    Me.ID = id
    Me.Name = name
  End Sub
End Class

Class Sample
  Shared Sub Main()
    ' Accountクラスの型情報を取得する
    Dim t As Type = GetType(Account)

    Console.WriteLine("Name = {0}", t.Name)
    Console.WriteLine("BaseType = {0}", t.BaseType)

    ' Accountクラスのすべてのメンバ情報を取得する
    ' (パブリックおよび非パブリックのインスタンスメンバを取得する)
    Dim members() As MemberInfo = t.GetMembers(BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance)

    For Each m As MemberInfo In members
      Console.WriteLine("MemberType = {0}, Name = {1}", m.MemberType, m.Name)
    Next
  End Sub
End Class
実行結果例
Name = Account
BaseType = System.Object
MemberType = Method, Name = get_ID
MemberType = Method, Name = set_ID
MemberType = Method, Name = get_Name
MemberType = Method, Name = set_Name
MemberType = Method, Name = Equals
MemberType = Method, Name = Finalize
MemberType = Method, Name = GetHashCode
MemberType = Method, Name = GetType
MemberType = Method, Name = MemberwiseClone
MemberType = Method, Name = ToString
MemberType = Method, Name = obj_address
MemberType = Constructor, Name = .ctor
MemberType = Property, Name = ID
MemberType = Property, Name = Name
MemberType = Field, Name = <ID>k__BackingField
MemberType = Field, Name = <Name>k__BackingField

インスタンスの操作

リフレクション機能を使うと、取得した型情報を使ってメソッドの呼び出しやフィールドの取得・設定などのインスタンスの操作をすることができます。 この際に、操作するメソッドやフィールド・プロパティは文字列によって指定することができます。 これにより、型に依存しない汎用的な処理を記述したり、実行時まで型が決定していないインスタンスに対する操作を行うといったことができます。

リフレクション機能を使ってインスタンスの生成・操作を行う
using System;
using System.Reflection;

class Account {
  public string Name { get; set; }
}

class Sample {
  static void Main()
  {
    // クラス名Accountの型情報を取得する
    var t = Type.GetType("Account");

    // 型情報からインスタンスを作成する
    var inst = Activator.CreateInstance(t);

    // Nameプロパティを取得する
    var p = t.GetProperty("Name");

    // プロパティに値を設定する
    p.SetValue(inst, "Alice", null);

    // プロパティの値を取得して表示する
    Console.WriteLine("Name = {0}", p.GetValue(inst, null));
  }
}
リフレクション機能を使ってインスタンスの生成・操作を行う
Imports System
Imports System.Reflection

Class Account
  Public Property Name As String
End Class

Class Sample
  Shared Sub Main()
    ' クラス名Accountの型情報を取得する
    Dim t As Type = Type.GetType("Account")

    ' 型情報からインスタンスを作成する
    Dim inst As Object = Activator.CreateInstance(t)

    ' Nameプロパティを取得する
    Dim p As PropertyInfo = t.GetProperty("Name")

    ' プロパティに値を設定する
    p.SetValue(inst, "Alice", Nothing)

    ' プロパティの値を取得して表示する
    Console.WriteLine("Name = {0}", p.GetValue(inst, Nothing))
  End Sub
End Class
実行結果
Name = Alice

このような操作については§.メンバの呼び出しで解説します。

属性

型情報以外のメタデータとして属性も挙げられます。 .NETでは属性を使うことでプログラムに型情報以外の追加的なメタデータを埋め込むことができ、それを実行時に取得・参照することができます。 属性にはコンパイラやランタイムが特別な操作を行うために使用されるものが存在するほか、独自に意味を定義して情報を埋め込むカスタム属性があります。

カスタム属性でメタデータを埋め込み、リフレクションにより埋め込んだメタデータを実行時に取得する
using System;
using System.Reflection;

// カスタム属性となるクラス
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class AuthorAttribute : Attribute {
  public string Name {
    get; private set;
  }

  public string Version;

  public AuthorAttribute(string name)
  {
    this.Name = name;
  }
}

class Sample {
  // カスタム属性が設定されたメソッド
  [Author("Alice", Version = "1.0")]
  [Author("Bob", Version = "1.1")]
  [Author("Charlie", Version = "1.2")]
  public static void TestMethod()
  {
  }

  static void Main()
  {
    // メソッドの情報を取得する
    var method = typeof(Sample).GetMethod("TestMethod");

    // メソッドに設定されているカスタム属性を取得して列挙する
    foreach (AuthorAttribute attr in method.GetCustomAttributes(typeof(AuthorAttribute), false)) {
      // 属性に設定されている値を表示する
      Console.WriteLine("Author: {0}, Version: {1}", attr.Name, attr.Version);
    }
  }
}
カスタム属性でメタデータを埋め込み、リフレクションにより埋め込んだメタデータを実行時に取得する
Imports System
Imports System.Reflection

' カスタム属性となるクラス
<AttributeUsage(AttributeTargets.Method, AllowMultiple := True)> _
Public Class AuthorAttribute
  Inherits Attribute

  Private _name As String

  Public Property Name As String
    Get
      Return _name
    End Get
    Set(ByVal value As String)
      _name = value
    End Set
  End Property

  Public Version As String

  Public Sub New(ByVal name As String)
    Me._name = name
  End Sub
End Class

Class Sample
  ' カスタム属性が設定されたメソッド
  <Author("Alice", Version := "1.0")> _
  <Author("Bob", Version := "1.1")> _
  <Author("Charlie", Version := "1.2")> _
  Public Shared Sub TestMethod()
  End Sub

  Shared Sub Main()
    ' メソッドの情報を取得する
    Dim method As MethodInfo = GetType(Sample).GetMethod("TestMethod")

    ' メソッドに設定されているカスタム属性を取得して列挙する
    For Each attr As AuthorAttribute In method.GetCustomAttributes(GetType(AuthorAttribute), False)
      ' 属性に設定されている値を表示する
      Console.WriteLine("Author: {0}, Version: {1}", attr.Name, attr.Version)
    Next
  End Sub
End Class
実行結果例
Author: Charlie, Version: 1.2
Author: Bob, Version: 1.1
Author: Alice, Version: 1.0

代表的な属性として、プログラムのバージョン情報を埋め込むための属性(アセンブリのバージョン情報を設定・取得する)や、旧式化されたAPIであることを示すObsolete属性(機能の廃止・非推奨化)、オブジェクトをシリアライズする際の動作を定義する属性(シリアライズの基本)などがあります。

その他、属性について、および属性の使用方法や独自に定義してその情報を取得する方法などについて詳しくは属性とメタデータで個別に解説しています。

リソース

型情報や属性のほかに、リソースもメタデータとして埋め込まれます。 リフレクションによってEXEやDLLに埋め込まれているリソースを利用することもできます。

リフレクションを使ったリソースの扱い方についてはリソースの埋め込みと読み込みで個別に解説しています。

型情報 (Typeクラス)

リフレクションにおいて中心的な役割を果たすTypeクラスについて解説します。 このクラスは型情報を扱うクラスとなっていて、このクラスから型自体の情報を取得できるほか、メソッドやフィールドなどメンバの情報の取得、型情報からのインスタンスの生成や、メソッド呼び出し・フィールドの書き換えなどのインスタンスの操作を行う上で必要な情報を取得することができます。

Typeクラスの取得 (typeof, GetType)

C#ではtypeof演算子、VBではGetType演算子を使うことで型情報をTypeクラスのインスタンスとして取得することができます。 また、任意の型のインスタンスでGetTypeメソッドを呼び出すことによっても取得することができます。 このメソッドはObjectクラスから継承されるため、どの型でも共通して使用することができます。

つまり、型名から直接型情報を取得したい場合にはtypeof/GetType、インスタンスからその型の型情報を取得したい場合にはGetTypeメソッドを使用します。

型情報を取得する
using System;

class Sample {
  static void Main()
  {
    var t1 = typeof(int); // int型の型情報の取得
    var t2 = typeof(Sample); // Sampleクラスの型情報の取得

    var str = "foo";

    var t3 = str.GetType(); // stringインスタンスからの型情報の取得
  }
}
型情報を取得する
Imports System

Class Sample
  Shared Sub Main()
    Dim t1 As Type = GetType(Integer) ' Integer型の型情報の取得
    Dim t2 As Type = GetType(Sample) ' Sampleクラスの型情報の取得

    Dim str As String = "foo"

    Dim t3 As Type = str.GetType() ' Stringインスタンスからの型情報の取得
  End Sub
End Class

ジェネリック型の型情報

ジェネリック型では、型引数に具体的な型が指定されているかどうかで型情報が変わります。 たとえばList<T>クラスを例にとった場合、型引数Tの型が具体的に定まっていないList<T>を特にオープンジェネリック型(あるいはジェネリック型定義)、対してList<int>List<string>など型引数Tの型が具体的な型が定まっているものをクローズジェネリック型(あるいは構築ジェネリック型構築された型)と呼びます。

typeof演算子・GetType演算子でジェネリック型の型情報を取得する場合、オープンジェネリック型とクローズジェネリック型を区別して取得することができます。 オープンジェネリック型を表す場合は型パラメータの指定を省略した型名(例:Dictionary<,>/Dictionary(Of ,))を使用し、一方クローズジェネリック型では具体的な型名を指定した型名(例:Dictionary<string, int>/Dictionary(Of String, Integer))を使用することによってそれぞれの型情報を取得することができます。

オープンジェネリック型およびクローズジェネリック型の型情報を取得する
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    var t1 = typeof(List<>); // 型パラメータが1つのオープンジェネリック型
    var t2 = typeof(List<int>); // 型パラメータが1つのクローズジェネリック型
    var t3 = typeof(List<string>);

    Console.WriteLine(t1);
    Console.WriteLine(t2);
    Console.WriteLine(t3);

    var t4 = typeof(Dictionary<,>); // 型パラメータが2つのオープンジェネリック型
    var t5 = typeof(Dictionary<string, int>); // 型パラメータが2つのクローズジェネリック型

    Console.WriteLine(t4);
    Console.WriteLine(t5);

    var t6 = typeof(Action<,>);
    var t7 = typeof(Action<,,>);
    var t8 = typeof(Action<,,,>);

    Console.WriteLine(t6);
    Console.WriteLine(t7);
    Console.WriteLine(t8);
  }
}
オープンジェネリック型およびクローズジェネリック型の型情報を取得する
Imports System
Imports System.Collections.Generic

Class Sample
  Shared Sub Main()
    Dim t1 As Type = GetType(List(Of)) ' 型パラメータが1つのオープンジェネリック型
    Dim t2 As Type = GetType(List(Of Integer)) ' 型パラメータが1つのクローズジェネリック型
    Dim t3 As Type = GetType(List(Of String))

    Console.WriteLine(t1)
    Console.WriteLine(t2)
    Console.WriteLine(t3)

    Dim t4 As Type = GetType(Dictionary(Of ,)) ' 型パラメータが2つのオープンジェネリック型
    Dim t5 As Type = GetType(Dictionary(Of String, Integer)) ' 型パラメータが2つのクローズジェネリック型

    Console.WriteLine(t4)
    Console.WriteLine(t5)

    Dim t6 As Type = GetType(Action(Of ,))
    Dim t7 As Type = GetType(Action(Of ,,))
    Dim t8 As Type = GetType(Action(Of ,,,))

    Console.WriteLine(t6)
    Console.WriteLine(t7)
    Console.WriteLine(t8)
  End Sub
End Class
実行結果
System.Collections.Generic.List`1[T]
System.Collections.Generic.List`1[System.Int32]
System.Collections.Generic.List`1[System.String]
System.Collections.Generic.Dictionary`2[TKey,TValue]
System.Collections.Generic.Dictionary`2[System.String,System.Int32]
System.Action`2[T1,T2]
System.Action`3[T1,T2,T3]
System.Action`4[T1,T2,T3,T4]

オープンジェネリック型(ジェネリック型定義)の型情報では、Type.MakeGenericTypeメソッドを使用することでクローズジェネリック型(構築ジェネリック型)を取得することができます。 言い換えると、MakeGenericTypeメソッドによってジェネリック型定義から構築ジェネリック型を構築することができます。

MakeGenericTypeメソッドの引数に型引数にしたい型の型情報(Type)を指定することで、対応するクローズジェネリック型の型情報を取得できます。 例えばDictionary<,>の型情報からDictionary<string, int>の型情報を取得したい場合は次のようにします。

オープンジェネリック型からクローズジェネリック型の型情報を取得する
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    var t1 = typeof(Dictionary<,>); // オープンジェネリック型

    Console.WriteLine(t1);

    // Dictionary<,>からDictionary<string, int>の型情報を作成する
    var t2 = t1.MakeGenericType(typeof(string), typeof(int));

    Console.WriteLine(t2);
  }
}
オープンジェネリック型からクローズジェネリック型の型情報を取得する
Imports System
Imports System.Collections.Generic

Class Sample
  Shared Sub Main()
    Dim t1 As Type = GetType(Dictionary(Of ,)) ' オープンジェネリック型

    Console.WriteLine(t1)

    ' Dictionary(Of ,)からDictionary(Of String, Integer)の型情報を作成する
    Dim t2 As Type = t1.MakeGenericType(GetType(String), GetType(Integer))

    Console.WriteLine(t2)
  End Sub
End Class
実行結果
System.Collections.Generic.Dictionary`2[TKey,TValue]
System.Collections.Generic.Dictionary`2[System.String,System.Int32]

逆にクローズジェネリック型(構築ジェネリック型)の型情報では、GetGenericTypeDefinitionメソッドを使用することでオープンジェネリック型(ジェネリック型定義)の型情報を取得することができます。 言い換えると、GetGenericTypeDefinitionメソッドによって構築ジェネリック型からジェネリック型定義を取得することができます。

例えばDictionary<string, int>の型情報からDictionary<,>の型情報を取得したい場合は次のようにします。

クローズジェネリック型からオープンジェネリック型の型情報を取得する
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    var t1 = typeof(Dictionary<string, int>); // クローズジェネリック型

    Console.WriteLine(t1);

    // Dictionary<string, int>からDictionary<,>の型情報を作成する
    var t2 = t1.GetGenericTypeDefinition();

    Console.WriteLine(t2);
  }
}
クローズジェネリック型からオープンジェネリック型の型情報を取得する
Imports System
Imports System.Collections.Generic

Class Sample
  Shared Sub Main()
    Dim t1 As Type = GetType(Dictionary(Of String, Integer)) ' クローズジェネリック型

    Console.WriteLine(t1)

    ' Dictionary(Of String, Integer)からDictionary(Of ,)の型情報を作成する
    Dim t2 As Type = t1.GetGenericTypeDefinition()

    Console.WriteLine(t2)
  End Sub
End Class
実行結果
System.Collections.Generic.Dictionary`2[System.String,System.Int32]
System.Collections.Generic.Dictionary`2[TKey,TValue]

.NETでは、ジェネリック型の型引数(型パラメータ、例えばList<T>におけるTList<int>におけるintの部分)もTypeクラスで扱います。 詳しくは後述の§.型パラメータ情報の取得で解説します。

ジェネリック型と型名の表記

Typeクラスなどで使われる型名の多くはコード上での表記と変わりませんが、ジェネリック型では異なる表記が使われます。 例えばコード上ではList<T>/List(Of T)と記述される型にはSystem.Collections.Generic.List`1といった表記が使われます。

型名では山括弧< >で型引数名を記述する代わりに、バッククオート`の後ろに型引数の数が記述されます。 例えばDictionary<TKey, TValue>では型引数の数が2つとなるため、System.Collections.Generic.Dictionary`2という表記になります。

ジェネリック型における型名の表記
using System;
using System.Reflection;

class Sample {
  static void Main()
  {
    Console.WriteLine(typeof(System.Collections.Generic.List<>));
    Console.WriteLine(typeof(System.Collections.Generic.List<int>));
    Console.WriteLine(typeof(System.Collections.Generic.Dictionary<,>));

    Console.WriteLine(typeof(System.Action));
    Console.WriteLine(typeof(System.Action<>));
    Console.WriteLine(typeof(System.Action<,>));
    Console.WriteLine(typeof(System.Action<,,>));
  }
}
ジェネリック型における型名の表記
Imports System
Imports System.Reflection

Class Sample
  Shared Sub Main()
    Console.WriteLine(GetType(System.Collections.Generic.List(Of )))
    Console.WriteLine(GetType(System.Collections.Generic.List(Of Integer)))
    Console.WriteLine(GetType(System.Collections.Generic.Dictionary(Of ,)))

    Console.WriteLine(GetType(System.Action))
    Console.WriteLine(GetType(System.Action(Of )))
    Console.WriteLine(GetType(System.Action(Of ,)))
    Console.WriteLine(GetType(System.Action(Of ,,)))
  End Sub
End Class
実行結果
System.Collections.Generic.List`1[T]
System.Collections.Generic.List`1[System.Int32]
System.Collections.Generic.Dictionary`2[TKey,TValue]
System.Action
System.Action`1[T]
System.Action`2[T1,T2]
System.Action`3[T1,T2,T3]

リフレクションにおいてジェネリック型の名前を文字列で指定する場合には、この表記を使用する必要があります。

アセンブリからの型情報の取得

アセンブリとは何かを簡略に説明すると、EXEファイルあるはDLLファイルから読み込まれた実行可能コードとメタデータのセットのことです。 (より正確には、実行可能コードとメタデータのセットであるモジュールを格納したもの) 特定のEXEやDLLで定義されている型情報を取得したい場合には、まずアセンブリを表すAssemblyクラスのインスタンスを取得する必要があります。

アセンブリの取得

Assemblyクラスのインスタンスを取得するには、取得したい対象のアセンブリに応じて次のメソッドを使うことができます。

アセンブリを取得するためのメソッド
メソッド 概要
Assembly.GetAssembly 引数で指定された型が宣言されているアセンブリを取得する
Assembly.GetExecutingAssembly 現在実行しているコードが存在するアセンブリを取得する
Assembly.GetCallingAssembly 現在実行しているコードを呼び出したアセンブリ(呼び出し元のアセンブリ)を取得する
Assembly.GetEntryAssembly 現在のプロセスのエントリーポイントとなっているアセンブリ(Mainメソッドがあるアセンブリ)を取得する
Assembly.Load 引数で指定された名前(アセンブリ名、あるいはその完全修飾名)を持つアセンブリ、あるいはメモリ上に展開されたアセンブリを読み込んで取得する
Assembly.LoadFrom 引数で指定されたパスにあるアセンブリファイルを読み込んで取得する
Assembly.ReflectionOnlyLoad Assembly.Loadと同様だが、リフレクションのみを目的として読み込み、取得する
Assembly.ReflectionOnlyLoadFrom Assembly.LoadFromと同様だが、リフレクションのみを目的として読み込み、取得する

アセンブリで定義されている型情報の取得

型名を指定した型情報の取得 (GetType)

アセンブリで定義されている型情報のうち、文字列で表された型名の型情報を取得したい場合はAssembly.GetTypeメソッドを使用します。 次の例では現在実行しているアセンブリから指定された名前の型情報を取得しています。 GetTypeメソッドに指定する型名は名前空間を含めた完全名を指定する必要があります。

文字列で表された型名から型情報を取得する
using System;
using System.Reflection;

namespace NS {
  class T1 {} // このクラスの完全名はNS.T1となる
}

class Sample {
  static void Main()
  {
    // 現在実行しているコードが含まれるアセンブリを取得する
    var a = Assembly.GetExecutingAssembly();

    // NS名前空間のクラスT1の型情報を取得する
    var t = a.GetType("NS.T1");
  }
}
文字列で表された型名から型情報を取得する
Imports System
Imports System.Reflection

Namespace NS
  ' このクラスの完全名はNS.T1となる
  Class T1
  End Class
End Namespace

Class Sample
  Shared Sub Main()
    ' 現在実行しているコードが含まれるアセンブリを取得する
    Dim a As [Assembly] = [Assembly].GetExecutingAssembly()

    ' NS名前空間のクラスT1の型情報を取得する
    Dim t As Type = a.GetType("NS.T1")
  End Sub
End Class

また、入れ子になっている型(クラス内で定義されたクラスなど)の型情報を取得したい場合は、目的の型を含んでいる型名と入れ子になっている型の名前を+で連結した型名を指定します。

入れ子になっている型の型情報を取得する
using System;
using System.Reflection;

namespace NS {
  class ContainerClass {
    class InnerClass {} // このクラスの完全名はNS.ContainerClass+InnerClassとなる
  }
}

class Sample {
  static void Main()
  {
    var a = Assembly.GetExecutingAssembly();

    // 入れ子になっているクラスInnerClassの型情報を取得する
    var t = a.GetType("NS.ContainerClass+InnerClass");
  }
}
入れ子になっている型の型情報を取得する
Imports System
Imports System.Reflection

Namespace NS
  Class ContainerClass
    ' このクラスの完全名はNS.ContainerClass+InnerClassとなる
    Class InnerClass
    End Class
  End Class
End Namespace

Class Sample
  Shared Sub Main()
    Dim a As [Assembly] = [Assembly].GetExecutingAssembly()

    ' 入れ子になっているクラスInnerClassの型情報を取得する
    Dim t As Type = a.GetType("NS.ContainerClass.InnerClass")
  End Sub
End Class

すべての型情報の取得 (GetTypes/GetExportedTypes)

アセンブリに定義されているすべての型情報を取得したい場合はAssembly.GetTypesメソッドあるいはAssembly.GetExportedTypesメソッドを使用します。

GetTypesメソッドではアセンブリに含まれるすべての型が返されるのに対して、GetExportedTypesメソッドではアセンブリの外部から参照できる型のみが返されます。 そのためGetExportedTypesメソッドでは、パブリックではない型(アクセス修飾子internal/Friendが指定されているクラスなど)は取得できません。

アセンブリ内のすべての型情報を取得する
using System;
using System.Reflection;

public class T1 {} // アセンブリ外に公開されるクラス(public)
internal class T2 {} // アセンブリ外に公開されないクラス(internal)

class Sample { // アセンブリ外に公開されないクラス(internal)
  static void Main()
  {
    var a = Assembly.GetExecutingAssembly();

    // GetTypesメソッドではすべての型が返される
    Console.WriteLine("[GetTypes]");

    foreach (var t in a.GetTypes()) {
      Console.WriteLine(t);
    }

    // GetExportedTypesメソッドではpublicな型のみが返される
    Console.WriteLine("[GetExportedTypes]");

    foreach (var t in a.GetExportedTypes()) {
      Console.WriteLine(t);
    }
  }
}
アセンブリ内のすべての型情報を取得する
Imports System
Imports System.Reflection

Public Class T1 : End Class ' アセンブリ外に公開されるクラス(Public)
Friend Class T2 : End Class ' アセンブリ外に公開されないクラス(Friend)

Class Sample ' アセンブリ外に公開されないクラス(internal)
  Shared Sub Main()
    Dim a As [Assembly] = [Assembly].GetExecutingAssembly()

    ' GetTypesメソッドではすべての型が返される
    Console.WriteLine("[GetTypes]")

    For Each t As Type In a.GetTypes()
      Console.WriteLine(t)
    Next

    ' GetExportedTypesメソッドではPublicな型のみが返される
    Console.WriteLine("[GetExportedTypes]")

    For Each t As Type In a.GetExportedTypes()
      Console.WriteLine(t)
    Next
  End Sub
End Class
実行結果
[GetTypes]
T1
T2
Sample
[GetExportedTypes]
T1

転送された型情報の取得 (GetForwardedTypes)

アセンブリに定義されている型のうち、別のアセンブリに転送された(forwarded)型を取得するには、Assembly.GetForwardedTypesメソッドを使うことができます。 このメソッドは、.NET Standard 2.1/.NET Core 2.1以降で使用できます。

このメソッドでは、属性TypeForwardedToAttribute/TypeForwardedFromAttributeによって転送された型が取得されます。 より具体的には、過去のバージョンではこのアセンブリで定義・実装されていたが、現在では他のアセンブリに実装が移された型が取得されます。

アセンブリから他アセンブリに転送された型情報を取得する .NET Standard 2.1/.NET Core 2.1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

class Sample {
  static void Main()
  {
    // System.Collections.dll(.NET Standard 1.6)をロードする
    var name = new AssemblyName("System.Collections, Version=4.0.10.0, PublicKeyToken=b03f5f7f11d50a3a");
    var assm = Assembly.Load(name);

    Console.WriteLine($"{name.FullName} -> {assm.GetName().FullName}");
    Console.WriteLine();

    // アセンブリで公開される型・他アセンブリへ転送される型の一覧と、転送先のアセンブリを表示する
    var forwardedTypes = new HashSet<Type>(assm.GetForwardedTypes());
    var exportedTypes = new HashSet<Type>(assm.GetExportedTypes());

    foreach (var t in exportedTypes.Union(forwardedTypes)) {
      Console.Write($"{t,-100}");
      Console.Write(forwardedTypes.Contains(t) ? "-> " : " @ ");
      Console.WriteLine(t.Assembly.GetName().FullName);
    }
  }
}
アセンブリから他アセンブリに転送された型情報を取得する .NET Standard 2.1/.NET Core 2.1
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Reflection

Class Sample
  Shared Sub Main()
    ' System.Collections.dll(.NET Standard 1.6)をロードする
    Dim name As New AssemblyName("System.Collections, Version=4.0.10.0, PublicKeyToken=b03f5f7f11d50a3a")
    Dim assm As [Assembly] = [Assembly].Load(name)

    Console.WriteLine($"{name.FullName} -> {assm.GetName().FullName}")
    Console.WriteLine()

    ' アセンブリで公開される型・他アセンブリへ転送される型の一覧と、転送先のアセンブリを表示する
    Dim forwardedTypes As New HashSet(Of Type)(assm.GetForwardedTypes())
    Dim exportedTypes As New HashSet(Of Type)(assm.GetExportedTypes())

    For Each t As Type In exportedTypes.Union(forwardedTypes)
      Console.Write($"{t,-100}")
      Console.Write(If(forwardedTypes.Contains(t), "-> ", " @ "))
      Console.WriteLine(t.[Assembly].GetName().FullName)
    Next
  End Sub
End Class
実行結果
System.Collections, Version=4.0.10.0, PublicKeyToken=b03f5f7f11d50a3a -> System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

System.Collections.BitArray                                                                          @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.StructuralComparisons                                                             @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.CollectionExtensions                                                      @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.LinkedList`1[T]                                                           @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.LinkedList`1+Enumerator[T]                                                @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.LinkedListNode`1[T]                                                       @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.Queue`1[T]                                                                @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.Queue`1+Enumerator[T]                                                     @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.SortedDictionary`2[TKey,TValue]                                           @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.SortedDictionary`2+Enumerator[TKey,TValue]                                @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.SortedDictionary`2+KeyCollection[TKey,TValue]                             @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.SortedDictionary`2+KeyCollection+Enumerator[TKey,TValue]                  @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.SortedDictionary`2+ValueCollection[TKey,TValue]                           @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.SortedDictionary`2+ValueCollection+Enumerator[TKey,TValue]                @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.SortedDictionary`2+KeyValuePairComparer[TKey,TValue]                      @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.TreeSet`1[T]                                                              @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.SortedList`2[TKey,TValue]                                                 @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.SortedList`2+KeyList[TKey,TValue]                                         @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.SortedList`2+ValueList[TKey,TValue]                                       @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.SortedSet`1[T]                                                            @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.SortedSet`1+Enumerator[T]                                                 @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.Stack`1[T]                                                                @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.Stack`1+Enumerator[T]                                                     @ System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Collections.Generic.Comparer`1[T]                                                            -> System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
System.Collections.Generic.Dictionary`2[TKey,TValue]                                                -> System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
System.Collections.Generic.Dictionary`2+Enumerator[TKey,TValue]                                     -> System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
System.Collections.Generic.Dictionary`2+KeyCollection[TKey,TValue]                                  -> System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
System.Collections.Generic.Dictionary`2+KeyCollection+Enumerator[TKey,TValue]                       -> System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
System.Collections.Generic.Dictionary`2+ValueCollection[TKey,TValue]                                -> System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
System.Collections.Generic.Dictionary`2+ValueCollection+Enumerator[TKey,TValue]                     -> System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
System.Collections.Generic.EqualityComparer`1[T]                                                    -> System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
System.Collections.Generic.HashSet`1[T]                                                             -> System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
System.Collections.Generic.HashSet`1+Enumerator[T]                                                  -> System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
System.Collections.Generic.List`1[T]                                                                -> System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
System.Collections.Generic.List`1+Enumerator[T]                                                     -> System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
System.Collections.Generic.ReferenceEqualityComparer                                                -> System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e

Typeクラスから取得できる情報

Typeクラスからは型名だけでなく、型がクラスなのかインターフェイスなのかといった型の種類や特徴に関する情報や、型で定義されているメソッド・プロパティ・フィールドなどに関する情報を取得することができます。

型情報の参照

Typeクラスでは次のようなプロパティによって型の種類に関する情報を参照・取得することができます。

Typeクラスのプロパティ
プロパティ 意味
型の名称 Name 名前空間を含まない型名
(例:System.IO.StreamならStream)
Namespace 名前空間
(例:System.IO.StreamならSystem.IO)
FullName 名前空間を含む型名
(例:System.IO.StreamならSystem.IO.Stream)
型の宣言箇所 Assembly 型が宣言されているアセンブリ
Module 型が宣言されているモジュール
DeclaringType 入れ子になっている型の場合、その型を含んでいる型
型の分類 IsClass 型がクラス型かどうか
IsValueType 型が値型(構造体・列挙体・プリミティブ数値型など)かどうか (関連:値型と参照型)
IsInterface 型がインターフェイス型かどうか
IsEnum 型が列挙体かどうか
IsArray 型が配列型かどうか
IsPrimitive 型がプリミティブ型かどうか (関連:型の種類・サイズ・精度・値域)
型の属性・状態・継承関係 IsAbstract 型が抽象型(abstract/MustInherit)かどうか
IsSealed 型がシールクラス(sealed/NotInheritable)かどうか
IsPublic 型がパブリックかどうか
IsNotPublic 型が非パブリックかどうか
IsNested 型が入れ子になっているか(他の型の内部で宣言されているか)
BaseType 基底クラス(継承元)の型情報 (関連:インスタンスや型が一致するか・インターフェイスやクラスから派生しているか判定する §.基底クラスの取得)
ジェネリック型の分類 IsGenericType ジェネリック型かどうか
(オープン型List<>とクローズ型List<int>のどちらでもtrueとなる)
IsGenericTypeDefinition ジェネリック型定義(オープンジェネリック型)かどうか
(オープン型List<>ではtrue、クローズ型List<int>ではfalseとなる)
IsConstructedGenericType
(.NET Framework 4.5以降)
構築ジェネリック型(クローズジェネリック型)かどうか
(オープン型List<>ではfalse、クローズ型List<int>ではtrueとなる)
ContainsGenericParameters 具体的な型が指定されていない型パラメーターを含むかどうか
(List<>ではtrue、List<int>ではfalseとなる
同様にDictionary<,>.KeyCollectionではtrue、Dictionary<int,int>.KeyCollectionではfalseとなる)
型パラメータの分類 IsGenericParameter 型がジェネリック型あるいはジェネリックメソッドの型パラメータを表すかどうか (§.型パラメータの分類)
IsGenericTypeParameter
(.NET Standard 2.1/.NET Core 2.1以降)
型がジェネリック型の型パラメータを表すかどうか (§.型パラメータの分類)
IsGenericMethodParameter
(.NET Standard 2.1/.NET Core 2.1以降)
型がジェネリックメソッドの型パラメータを表すかどうか (§.型パラメータの分類)
プロパティ 意味

次の例はTypeクラスのプロパティを参照して型の分類を行う例です。 この例で使用しているデリゲート型の判定に関しては後述の§.型がデリゲート型かどうか調べるを参照してください。

Typeクラスを使って型の分類を行う
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    ClassifyType(typeof(int));
    ClassifyType(typeof(string));
    ClassifyType(typeof(double[]));
    ClassifyType(typeof(DateTime));
    ClassifyType(typeof(KeyValuePair<,>));
    ClassifyType(typeof(DayOfWeek));
    ClassifyType(typeof(List<>));
    ClassifyType(typeof(ICloneable));
    ClassifyType(typeof(IEnumerable<>));
    ClassifyType(typeof(EventHandler));
    ClassifyType(typeof(Action<>));
  }

  static void ClassifyType(Type t)
  {
    Console.Write("{0}: ", t.FullName);

    if (t.IsValueType) {
      if (t.IsEnum) {
        Console.WriteLine("列挙体");
      }
      else {
        if (t.IsPrimitive) {
          Console.WriteLine("値型(プリミティブ)");
        }
        else {
          if (t.IsGenericType)
            Console.Write("ジェネリック");

          Console.WriteLine("構造体");
        }
      }
    }
    else {
      if (t.IsArray) {
        Console.WriteLine("配列型");
      }
      else {
        if (t.IsGenericType)
          Console.Write("ジェネリック");

        if (t.IsInterface) {
          Console.WriteLine("インターフェイス型");
        }
        else {
          if (IsDelegate(t))
            Console.WriteLine("デリゲート型");
          else
            Console.WriteLine("クラス型");
        }
      }
    }
  }

  static bool IsDelegate(Type t)
  {
    return t.IsSubclassOf(typeof(Delegate)) || t.Equals(typeof(Delegate));
  }
}
Typeクラスを使って型の分類を行う
Imports System
Imports System.Collections.Generic

Class Sample
  Shared Sub Main()
    ClassifyType(GetType(Integer))
    ClassifyType(GetType(String))
    ClassifyType(GetType(Double()))
    ClassifyType(GetType(DateTime))
    ClassifyType(GetType(KeyValuePair(Of ,)))
    ClassifyType(GetType(DayOfWeek))
    ClassifyType(GetType(List(Of)))
    ClassifyType(GetType(ICloneable))
    ClassifyType(GetType(IEnumerable(Of)))
    ClassifyType(GetType(EventHandler))
    ClassifyType(GetType(Action(Of)))
  End Sub

  Shared Sub ClassifyType(ByVal t As Type)
    Console.Write("{0}: ", t.FullName)

    If t.IsValueType Then
      If t.IsEnum Then
        Console.WriteLine("列挙体")
      Else
        If t.IsPrimitive Then
          Console.WriteLine("値型(プリミティブ)")
        Else
          If t.IsGenericType Then Console.Write("ジェネリック")

          Console.WriteLine("構造体")
        End If
      End If
    Else
      If t.IsArray Then
        Console.WriteLine("配列型")
      Else
        If t.IsGenericType Then Console.Write("ジェネリック")

        If t.IsInterface Then
          Console.WriteLine("インターフェイス型")
        Else
          If IsDelegate(t) Then
            Console.WriteLine("デリゲート型")
          Else
            Console.WriteLine("クラス型")
          End If
        End If
      End If
    End If
  End Sub

  Shared Function IsDelegate(ByVal t As Type) As Boolean
    Return t.IsSubclassOf(GetType([Delegate])) OrElse t.Equals(GetType([Delegate]))
  End Function
End Class
実行結果
System.Int32: 値型(プリミティブ)
System.String: クラス型
System.Double[]: 配列型
System.DateTime: 構造体
System.Collections.Generic.KeyValuePair`2: ジェネリック構造体
System.DayOfWeek: 列挙体
System.Collections.Generic.List`1: ジェネリッククラス型
System.ICloneable: インターフェイス型
System.Collections.Generic.IEnumerable`1: ジェネリックインターフェイス型
System.EventHandler: デリゲート型
System.Action`1: ジェネリックデリゲート型

型の種類・分類については型の種類・サイズ・精度・値域値型と参照型を合わせて参照してください。

型がデリゲート型かどうか調べる

Typeクラスには型がデリゲート型かどうかを調べるIsDelegateのようなプロパティは用意されていません。 Typeがデリゲート型を表すかどうかを調べたい場合は以下のような方法をとることができます。

Typeクラスを使って型がデリゲート型かどうかを調べる
using System;

class Sample {
  static bool IsDelegate(Type t)
  {
    // 型がDelegate型またはDelegate型の派生クラスの場合はデリゲート型と判定する
    return t.IsSubclassOf(typeof(Delegate)) || t.Equals(typeof(Delegate));
  }

  static void Main()
  {
    foreach (var t in new[] {
      typeof(EventHandler),
      typeof(Action<,>),
      typeof(Delegate),
      typeof(MulticastDelegate),
      typeof(ICloneable),
    }) {
      Console.WriteLine("IsDelegate({0}) = {1}", t, IsDelegate(t));
    }
  }
}
Typeクラスを使って型がデリゲート型かどうかを調べる
Imports System

Class Sample
  Shared Function IsDelegate(ByVal t As Type) As Boolean
    ' 型がDelegate型またはDelegate型の派生クラスの場合はデリゲート型と判定する
    Return t.IsSubclassOf(GetType([Delegate])) OrElse t.Equals(GetType([Delegate]))
  End Function

  Shared Sub Main()
    For Each t As Type In New Type() { _
      GetType(EventHandler), _
      GetType(Action(Of ,)), _
      GetType([Delegate]), _
      GetType([MulticastDelegate]), _
      GetType(ICloneable) _
    }
      Console.WriteLine("IsDelegate({0}) = {1}", t, IsDelegate(t))
    Next
  End Sub
End Class
実行結果
IsDelegate(System.EventHandler) = True
IsDelegate(System.Action`2[T1,T2]) = True
IsDelegate(System.Delegate) = True
IsDelegate(System.MulticastDelegate) = True
IsDelegate(System.ICloneable) = False

実装しているインターフェイスを調べる

型が実装しているすべてのインターフェイスを取得するにはGetInterfacesメソッドを使うことができます。

Typeクラスを使って型が実装しているインターフェイスを調べる
using System;
using System.Collections.Generic;
using System.Reflection;

class Sample {
  static void Main()
  {
    var t = typeof(List<int>);

    // List<int>クラスが実装するインターフェイスをすべて取得して表示する
    foreach (var ti in t.GetInterfaces()) {
      Console.WriteLine(ti);
    }
    Console.WriteLine();
  }
}
Typeクラスを使って型が実装しているインターフェイスを調べる
Imports System
Imports System.Collections.Generic
Imports System.Reflection

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(List(Of Integer))

    ' List(Of Integer)クラスが実装するインターフェイスをすべて取得して表示する
    For Each ti As Type In t.GetInterfaces()
      Console.WriteLine(ti)
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
System.Collections.Generic.IList`1[System.Int32]
System.Collections.Generic.IReadOnlyList`1[System.Int32]
System.Collections.ICollection
System.Collections.Generic.ICollection`1[System.Int32]
System.Collections.IEnumerable
System.Collections.Generic.IEnumerable`1[System.Int32]
System.Collections.Generic.IReadOnlyCollection`1[System.Int32]
System.Collections.IList

一方、型が特定のインターフェイスを実装しているかどうかについては、GetInterfaceメソッドでインターフェイス名を文字列で指定して調べるか、IsAssignableFromメソッドを使ってインターフェイス型への代入を行えるかどうかを調べることによって知ることができます。

Typeクラスを使って型が特定のインターフェイスを実装しているか・代入可能かどうか調べる
using System;
using System.Collections.Generic;
using System.Reflection;

class Sample {
  static void Main()
  {
    var t = typeof(List<int>);

    // List<int>がIEnumerable<T>インターフェイスを実装しているか調べる
    Console.WriteLine(t.GetInterface("System.Collections.Generic.IEnumerable`1") != null);

    // List<int>がIEnumerable<int>インターフェイスに代入可能かを調べる
    Console.WriteLine(typeof(IEnumerable<int>).IsAssignableFrom(t));
  }
}
Typeクラスを使って型が特定のインターフェイスを実装しているか・代入可能かどうか調べる
Imports System
Imports System.Collections.Generic
Imports System.Reflection

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(List(Of Integer))

    ' List(Of Integer)がIEnumerable(Of T)インターフェイスを実装しているか調べる
    Console.WriteLine(t.GetInterface("System.Collections.Generic.IEnumerable`1") IsNot Nothing)

    ' List(Of Integer)がIEnumerable(Of Integer)インターフェイスに代入可能かを調べる
    Console.WriteLine(GetType(IEnumerable(Of Integer)).IsAssignableFrom(t))
  End Sub
End Class
実行結果
True
True

ジェネリックインターフェイス型を指定する場合、オープンジェネリック型とクローズジェネリック型の違いに注意する必要があります。 ジェネリック型の文字列表記にも注意する必要があります。 詳しくは§.ジェネリック型の型情報の解説を参照してください。

型情報からインターフェイスの代入可能性を判定する方法についてはインスタンスや型が一致するか・インターフェイスやクラスから派生しているか判定するでも詳しく解説しています。

型パラメータ情報の取得

ジェネリック型の型パラメータ(例えばList<int>における< >内の部分)を取得したい場合はGetGenericArgumentsメソッドを使うことができます。 型パラメータの情報もTypeクラスとして取得されます。 Typeが型パラメータを表す場合、IsGenericParameterプロパティtrueとなります。

GetGenericArgumentsメソッドは、オープンジェネリック型・クローズジェネリック型のどちらの型情報に対しても用いることができます。

ジェネリック型における型パラメータの型情報を取得する
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    ClassifyGenericType(typeof(List<>));
    ClassifyGenericType(typeof(List<int>));
    ClassifyGenericType(typeof(List<string>));
    ClassifyGenericType(typeof(Dictionary<,>));
    ClassifyGenericType(typeof(Dictionary<string, int>));
    ClassifyGenericType(typeof(Action<,,,>));
    ClassifyGenericType(typeof(Action<int, float, double, string>));
    ClassifyGenericType(typeof(int));
  }

  static void ClassifyGenericType(Type t)
  {
    Console.Write("{0}: ", t.Name);

    if (t.IsGenericType) {
      // 型がジェネリック型の場合、ジェネリック型定義か構築ジェネリック型か調べる
      if (t.IsGenericTypeDefinition)
        Console.Write("ジェネリック型定義; ");
      else
        Console.Write("構築ジェネリック型; ");

      // ジェネリック型の型パラメータの型情報を取得して表示する
      foreach (var ta in t.GetGenericArguments()) {
        Console.Write("{0}, ", ta.Name);
      }
      Console.WriteLine();
    }
    else {
      Console.WriteLine("(非ジェネリック型)");
    }
  }
}
ジェネリック型における型パラメータの型情報を取得する
Imports System
Imports System.Collections.Generic

Class Sample
  Shared Sub Main()
    ClassifyGenericType(GetType(List(Of)))
    ClassifyGenericType(GetType(List(Of Integer)))
    ClassifyGenericType(GetType(List(Of String)))
    ClassifyGenericType(GetType(Dictionary(Of ,)))
    ClassifyGenericType(GetType(Dictionary(Of String, Integer)))
    ClassifyGenericType(GetType(Action(Of ,,,)))
    ClassifyGenericType(GetType(Action(Of Integer, Single, Double, String)))
    ClassifyGenericType(GetType(Integer))
  End Sub

  Shared Sub ClassifyGenericType(ByVal t As Type)
    Console.Write("{0}: ", t.Name)

    If t.IsGenericType Then
      ' 型がジェネリック型の場合、ジェネリック型定義か構築ジェネリック型か調べる
      If t.IsGenericTypeDefinition Then
        Console.Write("ジェネリック型定義; ")
      Else
        Console.Write("構築ジェネリック型; ")
      End If

      ' ジェネリック型の型パラメータの型情報を取得して表示する
      For Each ta As Type In t.GetGenericArguments()
        Console.Write("{0}, ", ta.Name)
      Next
      Console.WriteLine()
    Else
      Console.WriteLine("(非ジェネリック型)")
    End If
  End Sub
End Class
実行結果
List`1: ジェネリック型定義; T, 
List`1: 構築ジェネリック型; Int32, 
List`1: 構築ジェネリック型; String, 
Dictionary`2: ジェネリック型定義; TKey, TValue, 
Dictionary`2: 構築ジェネリック型; String, Int32, 
Action`4: ジェネリック型定義; T1, T2, T3, T4, 
Action`4: 構築ジェネリック型; Int32, Single, Double, String, 
Int32: (非ジェネリック型)

ジェネリックメソッドの型パラメータも同様に取得することができます。 この場合、まず対象となるジェネリックメソッドのMethodInfoを取得し、その後MethodInfo.GetGenericArgumentsメソッドを呼び出します。

ジェネリックメソッドの型パラメータを取得する
using System;
using System.Reflection;

class C {
  public void M<TArg1, TArg2>(TArg1 arg1, TArg2 arg2)
  {
  }
}

class Sample {
  static void Main()
  {
    // メソッドC.M<T>のメソッド情報を取得する
    var m = typeof(C).GetMethod("M");

    Console.Write("{0}: ", m.Name);

    foreach (var ta in m.GetGenericArguments()) {
      Console.Write("{0}, ", ta.Name);
    }
    Console.WriteLine();
  }
}
ジェネリックメソッドの型パラメータを取得する
Imports System
Imports System.Reflection

Class C
  Public Sub M(Of TArg1, TArg2)(ByVal arg1 As TArg1, ByVal arg2 As TArg2)
  End Sub
End Class

Class Sample
  Shared Sub Main()
    ' メソッドC.M<T>のメソッド情報を取得する
    Dim m As MethodInfo = GetType(C).GetMethod("M")

    Console.Write("{0}: ", m.Name)

    For Each ta As Type In m.GetGenericArguments()
      Console.Write("{0}, ", ta.Name)
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
M: TArg1, TArg2,

この例で使用しているGetMethodメソッドについては後述の§.メンバ情報の取得 (MemberInfo)で解説しています。 また、型パラメータを含むメソッドやジェネリックメソッドの取得に関しては§.型パラメータを指定した取得 (ジェネリック型のメソッド/ジェネリックメソッドの取得)を参照してください。


型パラメータの宣言されている型(またはメソッド)を参照する場合は次のプロパティによって取得することができます。 ジェネリック型の型パラメータを表すTypeではDeclaringTypeプロパティ、ジェネリックメソッドの型パラメータを表すTypeではDeclaringMethodプロパティを参照することにより、宣言されている型を取得できます。

型パラメータが宣言されている型あるいはメソッドを取得する
using System;
using System.Collections.Generic;
using System.Reflection;

class C {
  public void M<TArg1, TArg2>(TArg1 arg1, TArg2 arg2)
  {
  }
}

class Sample {
  static void Main()
  {
    // ジェネリック型List<T>の型パラメータTの型情報を取得する
    var arg1 = typeof(List<>).GetGenericArguments()[0];

    // 型パラメータが宣言されている型を表示する
    Console.WriteLine("{0} -> {1}", arg1, arg1.DeclaringType);

    // ジェネリックメソッドC.M<T>の型パラメータTの型情報を取得する
    var arg2 = typeof(C).GetMethod("M").GetGenericArguments()[0];

    // 型パラメータが宣言されているメソッドを表示する
    Console.WriteLine("{0} -> {1}", arg2, arg2.DeclaringMethod);
  }
}
型パラメータが宣言されている型あるいはメソッドを取得する
Imports System
Imports System.Collections.Generic
Imports System.Reflection

Class C
  Public Sub M(Of TArg1, TArg2)(ByVal arg1 As TArg1, ByVal arg2 As TArg2)
  End Sub
End Class

Class Sample
  Shared Sub Main()
    ' ジェネリック型List(Of T)の型パラメータTの型情報を取得する
    Dim arg1 As Type = GetType(List(Of)).GetGenericArguments()(0)

    ' 型パラメータが宣言されている型を表示する
    Console.WriteLine("{0} -> {1}", arg1, arg1.DeclaringType)

    ' ジェネリックメソッドC.M<T>の型パラメータTの型情報を取得する
    Dim arg2 As Type = GetType(C).GetMethod("M").GetGenericArguments()(0)

    ' 型パラメータが宣言されているメソッドを表示する
    Console.WriteLine("{0} -> {1}", arg2, arg2.DeclaringMethod)
  End Sub
End Class
実行結果
T -> System.Collections.Generic.List`1[T]
TArg1 -> Void M[TArg1,TArg2](TArg1, TArg2)

型パラメータの分類

ジェネリック型の型パラメータはType.GetGenericArgumentsメソッドによって取得することができ、ジェネリックメソッドの型パラメータはType.MakeGenericMethodParameterメソッド(.NET Standard 2.1/.NET Core 2.1以降)によって作成することができます。 (詳細:§.型パラメータを指定した取得 (ジェネリック型のメソッド/ジェネリックメソッドの取得))

Typeが型パラメータを表すかどうかは、IsGenericParameterプロパティによって判別することができます。 また、ジェネリック型の型パラメータかどうかはIsGenericTypeParameterプロパティジェネリックメソッドの型パラメータかどうかはIsGenericMethodParameterプロパティによって判別することができます。

Typeがジェネリック型の型パラメータか、ジェネリックメソッドの型パラメータかを調べる .NET Standard 2.1/.NET Core 2.1
using System;
using System.Reflection;

class C<T> {}

class Sample {
  static void Main()
  {
    var t = typeof(C<>);

    DisplayType(t); // ジェネリック型
    DisplayType(t.GetGenericArguments()[0]); // ジェネリック型の型パラメータ
    DisplayType(Type.MakeGenericMethodParameter(0)); // ジェネリックメソッドの型パラメータ
  }

  static void DisplayType(Type t)
  {
    Console.WriteLine($"[{t}]");
    Console.WriteLine($"{nameof(Type.IsGenericType)}? {t.IsGenericType}");
    Console.WriteLine($"{nameof(Type.IsGenericParameter)}? {t.IsGenericParameter}");
    Console.WriteLine($"{nameof(Type.IsGenericTypeParameter)}? {t.IsGenericTypeParameter}");
    Console.WriteLine($"{nameof(Type.IsGenericMethodParameter)}? {t.IsGenericMethodParameter}");
    Console.WriteLine();
  }
}
Typeがジェネリック型の型パラメータか、ジェネリックメソッドの型パラメータかを調べる .NET Standard 2.1/.NET Core 2.1
Imports System
Imports System.Reflection

Class C(Of T)
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C(Of))

    DisplayType(t) ' ジェネリック型
    DisplayType(t.GetGenericArguments()(0)) ' ジェネリック型の型パラメータ
    DisplayType(Type.MakeGenericMethodParameter(0)) ' ジェネリックメソッドの型パラメータ
  End Sub

  Shared Sub DisplayType(ByVal t As Type)
    Console.WriteLine($"[{t}]")
    Console.WriteLine($"{nameof(Type.IsGenericType)}? {t.IsGenericType}")
    Console.WriteLine($"{nameof(Type.IsGenericParameter)}? {t.IsGenericParameter}")
    Console.WriteLine($"{nameof(Type.IsGenericTypeParameter)}? {t.IsGenericTypeParameter}")
    Console.WriteLine($"{nameof(Type.IsGenericMethodParameter)}? {t.IsGenericMethodParameter}")
    Console.WriteLine()
  End Sub
End Class
実行結果
[C`1[T]]
IsGenericType? True
IsGenericParameter? False
IsGenericTypeParameter? False
IsGenericMethodParameter? False

[T]
IsGenericType? False
IsGenericParameter? True
IsGenericTypeParameter? True
IsGenericMethodParameter? False

[!!0]
IsGenericType? False
IsGenericParameter? True
IsGenericTypeParameter? False
IsGenericMethodParameter? True

メンバ情報の取得 (MemberInfo)

Typeクラスでは次のようなメソッドによって型で定義されているメンバの情報を取得することができます。 これらのメソッドを使うことによって、型情報からメソッドやフィールドなどのメンバを参照することができ、そこから更にメソッドの呼び出しやフィールドの値の取得など(詳細は§.MemberInfo派生クラスを使ったメンバの呼び出しで後述)を行うことができます。

Typeクラスのメソッド
メソッド 動作
GetMember
GetMembers
指定された名前のメンバ、あるいはすべてのメンバを取得する。
メンバ情報はMemberInfoクラスで返される。
GetConstructor
GetConstructors
指定された引数リストのコンストラクタ、あるいはすべてのメンバを取得する。
ConstructorInfoクラスが返される。
GetEvent
GetEvents
指定された名前のイベント、あるいはすべてのイベントを取得する。
EventInfoクラスが返される。
GetField
GetFields
指定された名前のフィールド、あるいはすべてのフィールドを取得する。
FieldInfoクラスが返される。
GetMethod
GetMethods
指定された名前・引数リストのメソッド、あるいはすべてのメソッドを取得する。
MethodInfoクラスが返される。
GetProperty
GetProperties
指定された名前のプロパティ、あるいはすべてのプロパティを取得する。
PropertyInfoクラスが返される。

戻り値として返されるMethodInfoなどのクラスはすべてMemberInfoクラスから派生したクラスとなっています。 MemberTypeプロパティを参照するとメンバの種類をMemberTypes列挙体で取得することができます。

クラスの型情報からすべてのメンバを取得する
using System;
using System.Reflection;

class C {
  public void M() {} // メソッド
  public int P { get; set; } // プロパティ
  public string F = null; // フィールド
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    // クラスCのすべてのメンバを取得して表示する
    foreach (var m in t.GetMembers()) {
      Console.WriteLine("{0}\t{1}", m.MemberType, m);
    }
    Console.WriteLine();

    // 名前がFのフィールドを取得する
    var f = t.GetField("F");

    Console.WriteLine(f);
  }
}
クラスの型情報からすべてのメンバを取得する
Imports System
Imports System.Reflection

Class C
  Public Sub M() ' メソッド
  End Sub

  Public Property P As Integer ' プロパティ
  Public F As String = Nothing ' フィールド
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    ' クラスCのすべてのメンバを取得して表示する
    For Each m As MemberInfo In t.GetMembers()
      Console.WriteLine("{0}{1}{2}", m.MemberType, vbTab, m)
    Next
    Console.WriteLine()

    ' 名前がFのフィールドを取得する
    Dim f As FieldInfo = t.GetField("F")

    Console.WriteLine(f)
  End Sub
End Class
実行結果
Method	Int32 get_P()
Method	Void set_P(Int32)
Method	Void M()
Method	Boolean Equals(System.Object)
Method	Int32 GetHashCode()
Method	System.Type GetType()
Method	System.String ToString()
Constructor	Void .ctor()
Property	Int32 P
Field	System.String F

System.String F

このようにして取得したMemberInfoを使うことにより、メソッドの呼び出しやフィールドの取得・設定などの操作を行うことができます。

メンバ情報の取得とBindingFlags

GetMembersなどメンバ情報を取得するメソッドでは、特に引数を指定しない場合はパブリックなインスタンスメンバのみを返します。 非パブリックやクラスのメンバ(静的メンバ)を取得したい場合はBindingFlagsを指定する必要があります。 BindingFlagsには次のような値が用意されています。

メンバ情報の取得とBindingFlags
意味 備考
BindingFlags.Static 静的メンバを対象とする どちらか一方または両方を指定する必要があります
BindingFlags.Instance インスタンスメンバを対象とする
BindingFlags.Public パブリックメンバを対象とする どちらか一方または両方を指定する必要があります
BindingFlags.NonPublic 非パブリックメンバを対象とする
BindingFlags.IgnoreCase メンバの名前を指定する際に、大文字小文字の違いを無視する
BindingFlags.DeclaredOnly その型で宣言されているメンバのみを対象とする (継承されたメンバを含めない)
BindingFlags.FlattenHierarchy 継承された静的メンバを対象とする
意味 備考
クラスの型情報から非パブリックなメンバのみを取得して表示する
using System;
using System.Reflection;

class C {
  public void M1() {}
  protected void M2() {}
  private void M3() {}
  private static void M4() {}

  public string F1 = null;
  private string F2 = null;
  private static string F3 = null;
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    // 非パブリックなインスタンスメンバのみを取得して列挙する
    foreach (var m in t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance)) {
      Console.WriteLine("{0}\t{1}", m.MemberType, m);
    }
  }
}
クラスの型情報から非パブリックなメンバのみを取得して表示する
Imports System
Imports System.Reflection

Class C
  Public Sub M1()
  End Sub

  Protected Sub M2()
  End Sub

  Private Sub M3()
  End Sub

  Private Shared Sub M4()
  End Sub

  Public F1 As String = Nothing
  Private F2 As String = Nothing
  Private Shared F3 As String = Nothing
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    ' 非パブリックなインスタンスメンバのみを取得して列挙する
    For Each m As MemberInfo In t.GetMembers(BindingFlags.NonPublic Or BindingFlags.Instance)
      Console.WriteLine("{0}{1}{2}", m.MemberType, vbTab, m)
    Next
  End Sub
End Class
実行結果
Method	Void M2()
Method	Void M3()
Method	Void Finalize()
Method	System.Object MemberwiseClone()
Field	System.String F2
継承を除外して対象のクラスで宣言されているメンバのみを取得する
using System;
using System.Reflection;

class C1 {
  public void M1() {}
}

class C2 : C1 {
  public void M2() {}
}

class Sample {
  static void Main()
  {
    var t = typeof(C2);

    // C2クラスで宣言されているメンバのみを取得して列挙する
    // (C2クラスが継承しているメンバを除外して取得する)
    foreach (var m in t.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)) {
      Console.WriteLine("{0}\t{1}", m.MemberType, m);
    }
  }
}
継承を除外して対象のクラスで宣言されているメンバのみを取得する
Imports System
Imports System.Reflection

Class C1
  Public Sub M1()
  End Sub
End Class

Class C2
  Inherits C1

  Public Sub M2()
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C2)

    ' C2クラスで宣言されているメンバのみを取得して列挙する
    ' (C2クラスが継承しているメンバを除外して取得する)
    For Each m As MemberInfo In t.GetMembers(BindingFlags.DeclaredOnly Or BindingFlags.Public Or BindingFlags.Instance)
      Console.WriteLine("{0}{1}{2}", m.MemberType, vbTab, m)
    Next
  End Sub
End Class
実行結果
Method	Void M2()
Constructor	Void .ctor()

プロパティのアクセサメソッドの取得

PropertyInfoからはプロパティのgetアクセサ/setアクセサに対応するMethodInfoをそれぞれ個別に取得することができます。 それぞれGetMethodプロパティ/SetMethodプロパティプロパティを参照することでMethodInfoとして取得できます。 読み取り専用/書き込み専用プロパティの場合はnull/Nothingが返されます。

PropertyInfoからプロパティのアクセサメソッドを取得する
using System;
using System.Reflection;

class C {
  public int P {get; set;}
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    var p = t.GetProperty("P"); // プロパティPのPropertyInfoを取得

    var getter = p.GetMethod; // プロパティPのgetアクセサメソッドを取得
    var setter = p.SetMethod; // プロパティPのsetアクセサメソッドを取得

    Console.WriteLine(getter);
    Console.WriteLine(setter);
  }
}
PropertyInfoからプロパティのアクセサメソッドを取得する
Imports System
Imports System.Reflection

Class C
  Public Property P As Integer
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    Dim p As PropertyInfo = t.GetProperty("P") ' プロパティPのPropertyInfoを取得

    Dim getter As MethodInfo = p.GetMethod ' プロパティPのGetアクセサメソッドを取得
    Dim setter As MethodInfo = p.SetMethod ' プロパティPのSetアクセサメソッドを取得

    Console.WriteLine(getter)
    Console.WriteLine(setter)
  End Sub
End Class
実行結果
Int32 get_P()
Void set_P(Int32)

引数リストを指定した取得

メソッドではオーバーロードによって同じ名前のメソッドが複数存在する場合があり、また特にコンストラクタのオーバーロードではコンストラクタは名前を持たないため、名前だけでなく引数リストを用いて対象のオーバーロードを指定する必要があります。

例えばGetMethodの場合、名前のみでメソッドを取得しようとしたときにオーバーロードが複数存在する場合は、例外AmbiguousMatchExceptionがスローされます。

取得しようとするメンバが限定できない場合、AmbiguousMatchExceptionがスローされる
using System;
using System.Reflection;

class C {
  public void M(int x) {}
  public void M(long x) {}
  public void M(int x, int y) {}
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    // 名前のみを指定してメソッドMのMethodInfoを取得しようとする
    // M(int)とM(long)とM(int, int)のどれが取得されるべき対象なのか曖昧なので、
    // 例外AmbiguousMatchExceptionがスローされる
    var m = t.GetMethod("M");
  }
}
取得しようとするメンバが限定できない場合、AmbiguousMatchExceptionがスローされる
Imports System
Imports System.Reflection

Class C
  Public Overloads Sub M(ByVal x As Integer)
  End Sub

  Public Overloads Sub M(ByVal x As Long)
  End Sub

  Public Overloads Sub M(ByVal x As Integer, ByVal y As Integer)
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    ' 名前のみを指定してメソッドMのMethodInfoを取得しようとする
    ' M(Integer)とM(Long)とM(Integer, Integer)のどれが取得されるべき対象なのか曖昧なので、
    ' 例外AmbiguousMatchExceptionがスローされる
    Dim m As MethodInfo = t.GetMethod("M")
  End Sub
End Class
実行結果
Unhandled exception. System.Reflection.AmbiguousMatchException: Ambiguous match found.
   at System.RuntimeType.GetMethodImplCommon(String name, Int32 genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConv, Type[] types, ParameterModifier[] modifiers)
   at System.RuntimeType.GetMethodImpl(String name, BindingFlags bindingAttr, Binder binder, CallingConventions callConv, Type[] types, ParameterModifier[] modifiers)
   at System.Type.GetMethod(String name, BindingFlags bindingAttr)
   at System.Type.GetMethod(String name)
   at Sample.Main()

このようにメンバ名だけでは単一のメンバを限定できない場合は、引数リストを指定することによって取得対象を限定することができます。

引数リストは次の例のようにTypeの配列で指定します。 引数がない場合は空の配列か、Type.EmptyTypesフィールドを指定します。 引数の型・数・順序のすべてに一致するものがなければnull/Nothingが返されます。

引数リストを指定してメソッドのオーバーロードを限定して取得する
using System;
using System.Reflection;

class C {
  public void M(int x) {}
  public void M(long x) {}
  public void M(int x, int y) {}
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    // int型の引数を1つとるメソッドMのオーバーロードを取得する
    var m1 = t.GetMethod("M", new[] {typeof(int)});

    // long型の引数を1つとるメソッドMのオーバーロードを取得する
    var m2 = t.GetMethod("M", new[] {typeof(long)});

    // int型の引数を2つとるメソッドMのオーバーロードを取得する
    var m3 = t.GetMethod("M", new[] {typeof(int), typeof(int)});

    Console.WriteLine(m1);
    Console.WriteLine(m2);
    Console.WriteLine(m3);
  }
}
引数リストを指定してメソッドのオーバーロードを限定して取得する
Imports System
Imports System.Reflection

Class C
  Public Overloads Sub M(ByVal x As Integer)
  End Sub

  Public Overloads Sub M(ByVal x As Long)
  End Sub

  Public Overloads Sub M(ByVal x As Integer, ByVal y As Integer)
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    ' Integer型の引数を1つとるメソッドMのオーバーロードを取得する
    Dim m1 As MethodInfo = t.GetMethod("M", New Type() {GetType(Integer)})

    ' Long型の引数を1つとるメソッドMのオーバーロードを取得する
    Dim m2 As MethodInfo = t.GetMethod("M", New Type() {GetType(Long)})

    ' Integer型の引数を2つとるメソッドMのオーバーロードを取得する
    Dim m3 As MethodInfo = t.GetMethod("M", New Type() {GetType(Integer), GetType(Integer)})

    Console.WriteLine(m1)
    Console.WriteLine(m2)
    Console.WriteLine(m3)
  End Sub
End Class
実行結果
Void M(Int32)
Void M(Int64)
Void M(Int32, Int32)
引数リストを指定して一致するコンストラクタを取得する
using System;
using System.Reflection;

class C {
  public C(int x) {}
  public C(string x, int y) {}
  public C() {}
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    // int型の引数を一つとるコンストラクタを取得する
    var ctor1 = t.GetConstructor(new[] {typeof(int)});

    // string型とint型の引数をとるコンストラクタを取得する
    var ctor2 = t.GetConstructor(new[] {typeof(string), typeof(int)});

    // 引数のないコンストラクタを取得する
    var ctor3 = t.GetConstructor(Type.EmptyTypes);

    Console.WriteLine(ctor1);
    Console.WriteLine(ctor2);
    Console.WriteLine(ctor3);
  }
}
引数リストを指定して一致するコンストラクタを取得する
Imports System
Imports System.Reflection

Class C
  Public Sub New(ByVal x As Integer)
  End Sub

  Public Sub New(ByVal x As String, ByVal y As Integer)
  End Sub

  Public Sub New()
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    ' Integer型の引数を一つとるコンストラクタを取得する
    Dim ctor1 As ConstructorInfo = t.GetConstructor(New Type() {GetType(Integer)})

    ' String型とInteger型の引数をとるコンストラクタを取得する
    Dim ctor2 As ConstructorInfo = t.GetConstructor(New Type() {GetType(String), GetType(Integer)})

    ' 引数のないコンストラクタを取得する
    Dim ctor3 As ConstructorInfo = t.GetConstructor(Type.EmptyTypes)

    Console.WriteLine(ctor1)
    Console.WriteLine(ctor2)
    Console.WriteLine(ctor3)
  End Sub
End Class
実行結果
Void .ctor(Int32)
Void .ctor(String, Int32)
Void .ctor()

ジェネリックメソッドなど、型パラメータによってメンバを限定する必要がある場合については§.型パラメータを指定した取得 (ジェネリック型のメソッド/ジェネリックメソッドの取得)を参照してください。

型パラメータを指定した取得 (ジェネリック型のメソッド/ジェネリックメソッドの取得)

ジェネリック型のメソッドやジェネリックメソッドでは型パラメータが引数として含まれる場合があり、引数リストを指定してメンバを取得する場合はそれを考慮する必要があります。

.NET Standard 2.1/.NET Core 2.1以降のType.GetMethodメソッドでは、型パラメータの数と引数リストを指定して特定のジェネリックメソッドを取得するためのオーバーロードが追加されています。

型パラメータTをとるジェネリックメソッドを考えたとき、このTに対応する型を引数リストに指定する場合は、Type.MakeGenericMethodParameterメソッドを使います。 このメソッドでは、型パラメータの位置を0から始まる値で指定することにより、その位置の型パラメータを表すTypeを返します。 例えば、型パラメータU, Vの2つをとるジェネリックメソッドであれば、位置が0ならU、位置が1ならVの型パラメータを表すTypeを返します。

Type.MakeGenericMethodParameterを使って型パラメータの位置に対応する型を作成し、引数リストとして指定する .NET Standard 2.1/.NET Core 2.1
using System;
using System.Reflection;

class C {
  public void M(int i) {}
  public void M<T>(T t) {}
  public void M<U, V>(U u, V v) {}
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    // メソッド名のみでは対象が限定できないため、AmbiguousMatchExceptionとなる
    //var ma = t.GetMethod("M"));

    // 引数リストが(int)のメソッドMを取得する
    var m1 = t.GetMethod("M", new[] {typeof(int)});

    // 引数リストが(long)のメソッドMを取得しようとする
    // M<T>のTをlongに型付けしたものが取得されるのではなく、M(long)を取得しようとするが、存在しないため、nullが返される
    var m2 = t.GetMethod("M", new[] {typeof(long)});

    // 型パラメータTをとり、引数リストが(T)のメソッドMを取得する
    // GetMethodの引数genericParameterCountに型パラメータの数として1を指定し、
    // 引数リストtypesにはType.MakeGenericMethodParameterメソッドで0番目の型パラメータに対応する型を作成して指定する
    var m3 = t.GetMethod(
      "M", // 名前が"M"のメソッドを対象とする
      genericParameterCount: 1, // 型パラメータの数が1のものを対象とする
      types: new[] {
        Type.MakeGenericMethodParameter(0) // Type.MakeGenericMethodParameterメソッドで0番目の型パラメータ(T)を表す型を作成して、引数リストとして指定する
      }
    );

    // 型パラメータU, Vをとり、引数リストが(U, V)のメソッドMを取得する
    var m4 = t.GetMethod(
      "M", // 名前が"M"のメソッドを対象とする
      genericParameterCount: 2, // 型パラメータの数が2のものを対象とする
      types: new[] {
        Type.MakeGenericMethodParameter(0), // 0番目の型パラメータ(U)を表す型
        Type.MakeGenericMethodParameter(1)  // 1番目の型パラメータ(V)を表す型
      }
    );

    Console.WriteLine($"{nameof(m1)} = {m1}");
    Console.WriteLine($"{nameof(m2)} = {m2}");
    Console.WriteLine($"{nameof(m3)} = {m3}");
    Console.WriteLine($"{nameof(m4)} = {m4}");
  }
}
Type.MakeGenericMethodParameterを使って型パラメータの位置に対応する型を作成し、引数リストとして指定する .NET Standard 2.1/.NET Core 2.1
Imports System
Imports System.Reflection

Class C
  Public Overloads Sub M(ByVal x As Integer)
  End Sub

  Public Overloads Sub M(Of T)(ByVal _t As T)
  End Sub

  Public Overloads Sub M(Of U, V)(ByVal _u As U, ByVal _v As V)
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    ' メソッド名のみでは対象が限定できないため、AmbiguousMatchExceptionとなる
    'Dim ma As MethodInfo = t.GetMethod("M")

    ' 引数リストが(Integer)のメソッドMを取得する
    Dim m1 As MethodInfo = t.GetMethod("M", New Type() {GetType(Integer)})

    ' 引数リストが(Long)のメソッドMを取得しようとする
    ' M(Of T)のTをLongに型付けしたものが取得されるのではなく、M(Long)を取得しようとするが、存在しないため、Nothingが返される
    Dim m2 As MethodInfo = t.GetMethod("M", New Type() {GetType(Long)})

    ' 型パラメータTをとり、引数リストが(T)のメソッドMを取得する
    ' GetMethodの引数genericParameterCountに型パラメータの数として1を指定し、
    ' 引数リストtypesにはType.MakeGenericMethodParameterメソッドで0番目の型パラメータに対応する型を作成して指定する
    Dim m3 As MethodInfo = t.GetMethod(
      "M", ' 名前が"M"のメソッドを対象とする
      genericParameterCount := 1, ' 型パラメータの数が1のものを対象とする
      types := New Type() {
        Type.MakeGenericMethodParameter(0) ' Type.MakeGenericMethodParameterメソッドで0番目の型パラメータ(T)を表す型を作成して、引数リストとして指定する
      }
    )

    ' 型パラメータU, Vをとり、引数リストが(U, V)のメソッドMを取得する
    Dim m4 As MethodInfo = t.GetMethod(
      "M", ' 名前が"M"のメソッドを対象とする
      genericParameterCount := 2, ' 型パラメータの数が2のものを対象とする
      types := New Type() {
        Type.MakeGenericMethodParameter(0), ' 0番目の型パラメータ(U)を表す型
        Type.MakeGenericMethodParameter(1)  ' 1番目の型パラメータ(V)を表す型
      }
    )

    Console.WriteLine($"{NameOf(m1)} = {m1}")
    Console.WriteLine($"{NameOf(m2)} = {m2}")
    Console.WriteLine($"{NameOf(m3)} = {m3}")
    Console.WriteLine($"{NameOf(m4)} = {m4}")
  End Sub
End Class
実行結果
m1 = Void M(Int32)
m2 = 
m3 = Void M[T](T)
m4 = Void M[U,V](U, V)

さらに、ジェネリック型のジェネリックメソッドでは、ジェネリック型の型パラメータ(class C<TC>TC)と、ジェネリックメソッドの型パラメータ(void M<TM>()TM)の2種類が引数に含まれる場合があります。

ジェネリック型の型パラメータはType.GetGenericArgumentsメソッドで取得することができ、ジェネリックメソッドの型パラメータはType.MakeGenericMethodParameterメソッドで得られるTypeで表すことができるため、この2つを組み合わせて引数リストとして指定します。

ジェネリック型とジェネリックメソッドそれぞれの型パラメータを取得して、引数リストとして指定する .NET Standard 2.1/.NET Core 2.1
using System;
using System.Reflection;

// 2つの型パラメータTCx, TCyを取るジェネリック型
class C<TCx, TCy> {
  // ジェネリック型の型パラメータを引数にとるメソッド
  public void M(TCx tcx) {}
  public void M(TCx tcx, TCy tcy) {}
  // ジェネリック型の型パラメータと、メソッドの型パラメータを引数にとるジェネリックメソッド
  public void M<TMx>(TCx tcx, TMx tmx) {}
  public void M<TMx, TMy>(TCy tcy, TMx tmx, TMy tmy) {}
}

class Sample {
  static void Main()
  {
    var t = typeof(C<,>);

    // 引数リストが(TCx)のメソッドを取得する
    var m1 = t.GetMethod("M", types: new[] {
      t.GetGenericArguments()[0] // 型パラメータTCx(ジェネリック型C<,>の0番目の型パラメータ)
    });

    // 引数リストが(TCx, TCy)のメソッドを取得する
    var m2 = t.GetMethod("M", types: new[] {
      t.GetGenericArguments()[0], // 型パラメータTCx(ジェネリック型C<,>の0番目の型パラメータ)のType
      t.GetGenericArguments()[1]  // 型パラメータTCy(ジェネリック型C<,>の1番目の型パラメータ)のType
    });

    // 引数リストが(TCx, TMx)のメソッドを取得する
    var m3 = t.GetMethod("M",
      genericParameterCount: 1, // 型パラメータが1つのジェネリックメソッド
      types: new[] {
        t.GetGenericArguments()[0],         // 型パラメータTCx(ジェネリック型C<,>の0番目の型パラメータ)のType
        Type.MakeGenericMethodParameter(0)  // 型パラメータTMx(ジェネリックメソッドM<>の0番目の型パラメータ)のType
      }
    );

    // 引数リストが(TCy, TMx, TMy)のメソッドを取得する
    var m4 = t.GetMethod("M",
      genericParameterCount: 2, // 型パラメータが2つのジェネリックメソッド
      types: new[] {
        t.GetGenericArguments()[1],         // 型パラメータTCy(ジェネリック型C<,>の1番目の型パラメータ)のType
        Type.MakeGenericMethodParameter(0), // 型パラメータTMx(ジェネリックメソッドM<,>の0番目の型パラメータ)のType
        Type.MakeGenericMethodParameter(1)  // 型パラメータTMy(ジェネリックメソッドM<,>の1番目の型パラメータ)のType
      }
    );

    Console.WriteLine($"{nameof(m1)} = {m1}");
    Console.WriteLine($"{nameof(m2)} = {m2}");
    Console.WriteLine($"{nameof(m3)} = {m3}");
    Console.WriteLine($"{nameof(m4)} = {m4}");
  }
}
ジェネリック型とジェネリックメソッドそれぞれの型パラメータを取得して、引数リストとして指定する .NET Standard 2.1/.NET Core 2.1
Imports System
Imports System.Reflection

' 2つの型パラメータTCx, TCyを取るジェネリック型
Class C(Of TCx, TCy)
  ' ジェネリック型の型パラメータを引数にとるメソッド
  Public Overloads Sub M(ByVal _tcx As TCx)
  End Sub

  Public Overloads Sub M(ByVal _tcx As TCx, ByVal _tcy As TCy)
  End Sub

  ' ジェネリック型の型パラメータと、メソッドの型パラメータを引数にとるジェネリックメソッド
  Public Overloads Sub M(Of TMx)(ByVal _tcx As TCx, ByVal _tmx As TMx)
  End Sub

  Public Overloads Sub M(Of TMx, TMy)(ByVal _tcy As TCy, ByVal _tmx As TMx, ByVal _tmy As TMy)
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C(Of ,))

    ' 引数リストが(TCx)のメソッドを取得する
    Dim m1 As MethodInfo = t.GetMethod("M", types := New Type() {
      t.GetGenericArguments()(0) ' 型パラメータTCx(ジェネリック型C(Of ,)の0番目の型パラメータ)
    })

    ' 引数リストが(TCx, TCy)のメソッドを取得する
    Dim m2 As MethodInfo = t.GetMethod("M", types := New Type() {
      t.GetGenericArguments()(0), ' 型パラメータTCx(ジェネリック型C(Of ,)の0番目の型パラメータ)のType
      t.GetGenericArguments()(1)  ' 型パラメータTCy(ジェネリック型C(Of ,)の1番目の型パラメータ)のType
    })

    ' 引数リストが(TCx, TMx)のメソッドを取得する
    Dim m3 As MethodInfo = t.GetMethod("M",
      genericParameterCount := 1, ' 型パラメータが1つのジェネリックメソッド
      New Type() {
        t.GetGenericArguments()(0),         ' 型パラメータTCx(ジェネリック型C(Of ,)の0番目の型パラメータ)のType
        Type.MakeGenericMethodParameter(0)  ' 型パラメータTMx(ジェネリックメソッドM(Of)の0番目の型パラメータ)のType
      }
    )

    ' 引数リストが(TCy, TMx, TMy)のメソッドを取得する
    Dim m4 As MethodInfo = t.GetMethod("M",
      genericParameterCount := 2, ' 型パラメータが2つのジェネリックメソッド
      New Type() {
        t.GetGenericArguments()(1),         ' 型パラメータTCy(ジェネリック型C(Of ,)の1番目の型パラメータ)のType
        Type.MakeGenericMethodParameter(0), ' 型パラメータTMx(ジェネリックメソッドM(Of ,)の0番目の型パラメータ)のType
        Type.MakeGenericMethodParameter(1)  ' 型パラメータTMy(ジェネリックメソッドM(Of ,)の1番目の型パラメータ)のType
      }
    )

    Console.WriteLine($"{NameOf(m1)} = {m1}")
    Console.WriteLine($"{NameOf(m2)} = {m2}")
    Console.WriteLine($"{NameOf(m3)} = {m3}")
    Console.WriteLine($"{NameOf(m4)} = {m4}")
  End Sub
End Class
実行結果
m1 = Void M(TCx)
m2 = Void M(TCx, TCy)
m3 = Void M[TMx](TCx, TMx)
m4 = Void M[TMx,TMy](TCy, TMx, TMy)

ジェネリック型のメソッド・ジェネリックメソッドのMethodInfoを使ってメソッド呼び出しを行う方法については§.ジェネリック型のメソッド・ジェネリックメソッドを参照してください。

Typeが型パラメータを表すものかどうか、またTypeがジェネリック型の型パラメータか、ジェネリックメソッドの型パラメータかどうかを判別する方法については§.型パラメータの分類を参照してください。

Type.MakeGenericMethodParameterを使用することができない.NET Standard 2.1/.NET Core 2.1より前やそれ以外の環境では、Type.GetMethodsメソッドを使って候補となるメソッドの一覧を取得し、そこから該当するメソッドを探し出す必要があります。

インスタンスの操作

.NETではリフレクションによって型情報を取得するだけでなく、取得した型情報を使ってインスタンスを作成したり、フィールドの書き換えやメソッドの呼び出しなどインスタンスに対する操作を行うこともできるようになっています。

インスタンスの作成 (Activator.CreateInstance)

Activator.CreateInstanceメソッドを使用すると、型情報を使って動的にインスタンスを作成することができます。

このメソッドではインスタンスを生成する際、コンストラクタに渡す引数を指定することができます。 コンストラクタに渡す引数はobject型の配列で指定します。 指定された引数に応じて適切なコンストラクタが自動的に呼び出されます。 当然、抽象クラスのインスタンスを作成しようとした場合や、引数と一致するコンストラクタがない場合は例外がスローされます。

Activator.CreateInstanceを使って型情報からインスタンスを作成する
using System;
using System.Reflection;

class C {
  private int f;

  public C()
  {
    this.f = 0;
  }

  public C(int f)
  {
    this.f = f;
  }

  public override string ToString()
  {
    return string.Format("f = {0}", f);
  }
}

class Sample {
  static void Main()
  {
    object inst;

    // デフォルト(引数なし)のコンストラクタを使ってクラスCのインスタンスを作成する
    inst = Activator.CreateInstance(typeof(C));

    Console.WriteLine(inst);

    // int型の引数を1つとるコンストラクタを使ってクラスCのインスタンスを作成する
    var args = new object[] {42};

    inst = Activator.CreateInstance(typeof(C), BindingFlags.CreateInstance, null, args, null);

    Console.WriteLine(inst);
  }
}
Activator.CreateInstanceを使って型情報からインスタンスを作成する
Imports System
Imports System.Reflection

Class C
  Private f As Integer

  Public Sub New()
    Me.f = 0
  End Sub

  Public Sub New(ByVal f As Integer)
    Me.f = f
  End Sub

  Public Overrides Function ToString() As String
    Return String.Format("f = {0}", f)
  End Function
End Class

Class Sample
  Shared Sub Main()
    Dim inst As Object

    ' デフォルト(引数なし)のコンストラクタを使ってクラスCのインスタンスを作成する
    inst = Activator.CreateInstance(GetType(C))

    Console.WriteLine(inst)

    ' Integer型の引数を1つとるコンストラクタを使ってクラスCのインスタンスを作成する
    Dim args As Object() = New Object() {42}

    inst = Activator.CreateInstance(GetType(C), BindingFlags.CreateInstance, Nothing, args, Nothing)

    Console.WriteLine(inst)
  End Sub
End Class
実行結果
f = 0
f = 42

CreateInstanceメソッドで作成したインスタンスはobjectで返されます。 そのためインスタンスを使用する際、既知の型の場合はその型にキャストするか、あるいはobject型のまま後述する方法でリフレクションによって操作を行います。

Activator.CreateInstanceで作成したインスタンスをリフレクションによって操作する
using System;
using System.Reflection;

class C {
  public void M()
  {
    Console.WriteLine("Hello, world!");
  }
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    var inst = Activator.CreateInstance(t);

    // InvokeMemberメソッドを使って作成したインスタンスのメソッドMを呼び出す
    t.InvokeMember("M", BindingFlags.InvokeMethod, null, inst, null);

    // インスタンスをキャストして直接メソッドMを呼び出す
    var c = (C)inst;

    c.M();
  }
}
Activator.CreateInstanceで作成したインスタンスをリフレクションによって操作する
Imports System
Imports System.Reflection

Class C
  Public Sub M()
    Console.WriteLine("Hello, world!")
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    Dim inst As Object = Activator.CreateInstance(t)

    ' 作成したインスタンスのメソッドMを呼び出す
    t.InvokeMember("M", BindingFlags.InvokeMethod, Nothing, inst, Nothing)

    ' インスタンスをキャストして直接メソッドMを呼び出す
    Dim c As C = DirectCast(inst, C)

    c.M()
  End Sub
End Class
実行結果
Hello, world!
Hello, world!

このように、型情報が取得できれば、動的にインスタンスを作成して操作することができます。 また、動的にアセンブリを読み込み、読み込んだアセンブリに含まれるインスタンスを作成するといったことも可能です。

Activator.CreateInstanceメソッドのほか、Type.InvokeMemberメソッドConstructorInfo.Invokeメソッドを使うことによっても、型情報からインスタンスを作成することができます。

Activator.CreateInstanceとパフォーマンス

Activator.CreateInstanceによるインスタンスの作成は、コンストラクタを直接呼び出す場合と比較するとオーバーヘッドが大きいため、パフォーマンスに影響します。 インスタンス作成にかかる時間を計測して比較すると次のようになります。 コンストラクタを使ったインスタンス作成が行える場合は、そちらを優先すべきです。

インスタンスの生成に限らず、一般にリフレクションによる操作には大きなオーバーヘッドが伴います。

Ubuntu 20.04 + .NET 5 RC2での実行結果
               new(): 00:00:00.0111939
      CreateInstance: 00:00:00.0542074
               new(): 00:00:00.0100221
      CreateInstance: 00:00:00.0515755
               new(): 00:00:00.0107415
      CreateInstance: 00:00:00.0559370
               new(): 00:00:00.0105062
      CreateInstance: 00:00:00.0543031
               new(): 00:00:00.0108950
      CreateInstance: 00:00:00.0494231
Ubuntu 20.04 + Mono 6.12での比較結果
               new(): 00:00:00.0087123
      CreateInstance: 00:00:00.4880726
               new(): 00:00:00.0067173
      CreateInstance: 00:00:00.4860424
               new(): 00:00:00.0065699
      CreateInstance: 00:00:00.4863571
               new(): 00:00:00.0066983
      CreateInstance: 00:00:00.4885523
               new(): 00:00:00.0069056
      CreateInstance: 00:00:00.4883043
Windows 7 + .NET Framework 4.5での比較結果
               new(): 00:00:00.0213256
      CreateInstance: 00:00:00.1682794
               new(): 00:00:00.0206288
      CreateInstance: 00:00:00.1625131
               new(): 00:00:00.0195052
      CreateInstance: 00:00:00.1609271
               new(): 00:00:00.0203098
      CreateInstance: 00:00:00.1609503
               new(): 00:00:00.0205671
      CreateInstance: 00:00:00.1606695
計測に用いたコード
using System;
using System.Diagnostics;

public class C {
  public C() {}
}

public class Test {
  static void Main()
  {
    for (var act = 0; act < 5; act++) {
      var sw1 = Stopwatch.StartNew();

      for (var i = 0; i < 1000 * 1000; i++) {
        new C();
      }

      sw1.Stop();

      Console.WriteLine("{0, 20}: {1}", "new()", sw1.Elapsed);

      var t = typeof(C);
      var sw2 = Stopwatch.StartNew();

      for (var i = 0; i < 1000 * 1000; i++) {
        Activator.CreateInstance(t);
      }

      sw2.Stop();

      Console.WriteLine("{0, 20}: {1}", "CreateInstance", sw2.Elapsed);
    }
  }
}

メンバの呼び出し

リフレクションによって型のメンバの呼び出しを行うことができます。 たとえば、メソッドの呼び出しやフィールド・プロパティの値の取得・設定ができます。 メンバの呼び出しには、Type.InvokeMemberメソッドによってメンバ名を指定して呼び出す方法と、Typeから取得したMethodInfo・PropertyInfoなどのメソッドを使って呼び出す方法があります。

Type.InvokeMemberを使ったメンバの呼び出し

Type.InvokeMemberメソッドを使うと型情報とメンバ名によってメンバの呼び出しを行うことができます。

InvokeMemberメソッドでは、メソッドの呼び出しのほか、プロパティの値の取得・設定、フィールドの値の取得・設定を行うことができます。 またコンストラクタを呼び出すことによってインスタンスを作成することもできます。 呼び出すメンバに応じて、次のBindingFlagsのいずれかを指定します。

InvokeMemberメソッドとBindingFlags
BindingFlags InvomeMemberメソッドの動作
BindingFlags.InvokeMethod メソッドの呼び出し
指定した名前・引数と一致するメソッドを呼び出して戻り値を取得する
BindingFlags.SetProperty プロパティの設定
指定した名前のプロパティに値を設定する
BindingFlags.GetProperty プロパティの取得
指定した名前のプロパティの値を取得する
BindingFlags.SetField フィールドの設定
指定した名前のフィールドに値を設定する
BindingFlags.GetField フィールドの取得
指定した名前のフィールドの値を取得する
BindingFlags.CreateInstance コンストラクタの呼び出し
指定した引数と一致するコンストラクタを呼び出してインスタンスを作成する

これらの値に加えて、非パブリックメンバを呼び出したり、メンバ名の大文字小文字の違いを無視して呼び出せるようにするためにBindingFlags.NonPublicやBindingFlags.IgnoreCaseなどを組み合わせて指定することもできます。

InvokeMemberメソッドでは、引数targetに操作の対象となるインスタンスを指定します。 静的メンバ(static/Shared)の場合は、インスタンスの代わりにnull/Nothingを指定します。

メソッドおよびコンストラクタの呼び出し時に渡す引数や、フィールド・プロパティに設定する値は、引数argsobject型の配列に格納した上で指定します。 引数がない場合はnull/Nothingを指定することができます。

また、BindingFlags.InvokeMethodで呼び出したメソッドの戻り値や、BindingFlags.CreateInstanceで作成したインスタンス、BindingFlags.GetPropertyおよびBindingFlags.GetFieldで取得したプロパティ・フィールドの値はInvokeMemberメソッドの戻り値として取得することができます。

Type.InvokeMemberを使ってメンバの呼び出しを行う
using System;
using System.Reflection;

class C {
  public string M(string s)
  {
    return s.ToUpper();
  }

  public int P { get; set; }

  public double F = 0.0;
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    var inst = Activator.CreateInstance(t);

    // インスタンスのメソッドMを呼び出す
    var ret = t.InvokeMember("M", BindingFlags.InvokeMethod, null, inst, new object[] {"Hello, world!"});

    Console.WriteLine(ret); // メソッドの戻り値を表示する

    // プロパティPに値42を設定する
    t.InvokeMember("P", BindingFlags.SetProperty, null, inst, new object[] {42});

    // プロパティPの値を取得する
    var p = t.InvokeMember("P", BindingFlags.GetProperty, null, inst, null);

    Console.WriteLine("P = {0}", p);

    // フィールドFに値3.14を設定する
    t.InvokeMember("F", BindingFlags.SetField, null, inst, new object[] {3.14});

    // フィールドFの値を取得する
    var f = t.InvokeMember("F", BindingFlags.GetField, null, inst, null);

    Console.WriteLine("F = {0}", f);
  }
}
Type.InvokeMemberを使ってメンバの呼び出しを行う
Imports System
Imports System.Reflection

Class C
  Public Function M(ByVal s As String) As String
    Return s.ToUpper()
  End Function

  Public Property P As Integer

  Public F As Double = 0.0
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    Dim inst As Object = Activator.CreateInstance(t)

    ' インスタンスのメソッドMを呼び出す
    Dim ret As Object = t.InvokeMember("M", BindingFlags.InvokeMethod, Nothing, inst, New Object() {"Hello, world!"})

    Console.WriteLine(ret) ' メソッドの戻り値を表示する

    ' プロパティPに値42を設定する
    t.InvokeMember("P", BindingFlags.SetProperty, Nothing, inst, New Object() {42})

    ' プロパティPの値を取得する
    Dim p As Object = t.InvokeMember("P", BindingFlags.GetProperty, Nothing, inst, Nothing)

    Console.WriteLine("P = {0}", p)

    ' フィールドFに値3.14を設定する
    t.InvokeMember("F", BindingFlags.SetField, Nothing, inst, New Object() {3.14})

    ' フィールドFの値を取得する
    Dim f As Object = t.InvokeMember("F", BindingFlags.GetField, Nothing, inst, Nothing)

    Console.WriteLine("F = {0}", f)
  End Sub
End Class
実行結果
HELLO, WORLD!
P = 42
F = 3.14
Type.InvokeMemberを使ってインスタンスの作成を行う
using System;
using System.Reflection;

class C {
  private string str = "Hello, world!";

  public C()
  {
  }

  public C(string str)
  {
    this.str = str;
  }

  public override string ToString()
  {
    return str;
  }
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    object inst;

    // コンストラクタに渡す引数を指定せずにインスタンスを作成する
    // (引数のないコンストラクタを使ってインスタンスを作成する)
    inst = t.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);

    Console.WriteLine(inst);

    // コンストラクタに渡す引数を指定してインスタンスを作成する
    // (引数のあるコンストラクタを使ってインスタンスを作成する)
    inst = t.InvokeMember(null, BindingFlags.CreateInstance, null, null, new object[] {"Hello, instance!"});

    Console.WriteLine(inst);
  }
}
Type.InvokeMemberを使ってインスタンスの作成を行う
Imports System
Imports System.Reflection

Class C
  Private str As String = "Hello, world!"

  Public Sub New()
  End Sub

  Public Sub New(ByVal str As String)
    Me.str = str
  End Sub

  Public Overrides Function ToString() As String
    Return str
  End Function
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    Dim inst As Object

    ' コンストラクタに渡す引数を指定せずにインスタンスを作成する
    ' (引数のないコンストラクタを使ってインスタンスを作成する)
    inst = t.InvokeMember(Nothing, BindingFlags.CreateInstance, Nothing, Nothing, Nothing)

    Console.WriteLine(inst)

    ' コンストラクタに渡す引数を指定してインスタンスを作成する
    ' (引数のあるコンストラクタを使ってインスタンスを作成する)
    inst = t.InvokeMember(Nothing, BindingFlags.CreateInstance, Nothing, Nothing, New Object() {"Hello, instance!"})

    Console.WriteLine(inst)
  End Sub
End Class
実行結果
Hello, world!
Hello, instance!

ジェネリック型のメソッドあるいはジェネリックメソッドを呼び出す場合については、§.ジェネリック型のメソッド・ジェネリックメソッドを参照してください。

メンバ呼び出しの結果例外が発生した場合、例外はTargetInvocationExceptionにラップされた上でスローされます。 TargetInvocationExceptionから実際にスローされた例外を取得する方法については§.TargetInvocationException、TargetInvocationExceptionにラップせずにスローさせる方法については§.BindingFlags.DoNotWrapExceptionsを参照してください。

MemberInfo派生クラスを使ったメンバの呼び出し

MethodInfoやPropertyInfoなどのMemberInfo派生クラスを取得し、以下のメソッドを使うことでもメンバの呼び出しを行うことができます。 MethodInfoやPropertyInfoの取得にはGetMethodやGetPropertyなどのメソッドを使用します。

メンバの呼び出しを行うメソッド
メソッド 動作
MethodInfo.Invoke 指定した引数と一致するメソッドを呼び出して戻り値を取得する
PropertyInfo.SetValue プロパティに値を設定する
PropertyInfo.GetValue プロパティの値を取得する
FieldInfo.SetValue フィールドに値を設定する
FieldInfo.GetValue フィールドの値を取得する
ConstructorInfo.Invoke 指定した引数と一致するコンストラクタを呼び出してインスタンスを作成する

コンストラクタ呼び出しの場合を除き、Type.InvokeMemberメソッドによるメンバ呼び出しの場合と同様に操作の対象となるインスタンスを引数objで指定します。 静的メンバ(static/Shared)の場合はインスタンスを指定する代わりにnull/Nothingを指定します。

また、メソッドおよびコンストラクタの呼び出し時に渡す引数は、object型の配列に格納して指定します。 フィールド・プロパティに設定する値をobject型の配列に格納して指定するInvokeMemberメソッドとは異なり、PropertyInfo.SetValueメソッド・FieldInfo.SetValueメソッドでは設定する値を直接引数として指定することができます。

MemberInfoを使ってメンバの呼び出しを行う
using System;
using System.Reflection;

class C {
  public string M(string s)
  {
    return s.ToUpper();
  }

  public double F = 0.0;
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    var inst = Activator.CreateInstance(t);

    // メソッドMのMethodInfoを取得する
    var m = t.GetMethod("M");

    // メソッドを呼び出す
    var ret = m.Invoke(inst, new object[] {"Hello, world!"});

    Console.WriteLine(ret); // メソッドの戻り値を表示する

    // フィールドFのFieldInfoを取得する
    var f = t.GetField("F");

    // フィールドに値3.14を設定する
    f.SetValue(inst, 3.14);

    // フィールドの値を取得する
    Console.WriteLine("F = {0}", f.GetValue(inst));
  }
}
MemberInfoを使ってメンバの呼び出しを行う
Imports System
Imports System.Reflection

Class C
  Public Function M(ByVal s As String) As String
    Return s.ToUpper()
  End Function

  Public F As Double = 0.0
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    Dim inst As Object = Activator.CreateInstance(t)

    ' メソッドMのMethodInfoを取得する
    Dim m As MethodInfo = t.GetMethod("M")

    ' メソッドを呼び出す
    Dim ret As Object = m.Invoke(inst, New Object() {"Hello, world!"})

    Console.WriteLine(ret) ' メソッドの戻り値を表示する

    ' フィールドFのFieldInfoを取得する
    Dim f As FieldInfo = t.GetField("F")

    ' フィールドに値3.14を設定する
    f.SetValue(inst, 3.14)

    ' フィールドの値を取得する
    Console.WriteLine("F = {0}", f.GetValue(inst))
  End Sub
End Class
実行結果
HELLO, WORLD!
F = 3.14

これらのメソッドでは、メンバの呼び出しを行うインスタンスや設定する値はすべてobject型の引数に渡します。 この際、構造体など値型インスタンスのフィールド・プロパティを設定する際には問題が生じる場合があります。

この問題と回避方法に関してはリフレクションを使って構造体フィールドに値を設定する(FieldInfo.SetValue)で解説しているので、合わせてご覧ください。

MethodInfoを使ったメソッド呼び出し

ref/ByRefパラメータ

メソッドの引数にout/ref修飾子、ByRef修飾子が設定されている場合、引数は参照渡しとなり、メソッドは引数の値を別の値に置き換えることができます。 MethodInfo.Invokeメソッドを使ってメソッドを呼び出す場合、参照渡しによって置き換えられた値は引数parametersに渡した配列に格納されます。

MethodInfoでのメソッド呼び出しで参照渡しの引数を参照する
using System;
using System.Reflection;

class C {
  public void M(int x, ref int y)
  {
    Console.WriteLine("x = {0}", x);
    Console.WriteLine("y = {0}", y);

    x = 3; // 値渡し引数の値を変更する (呼び出し元には反映されない)
    y = 3; // 参照渡し引数の値を変更する (呼び出し元に反映される)
  }
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    var inst = Activator.CreateInstance(t);

    var m = t.GetMethod("M");

    // メソッド呼び出しに使用する引数の配列
    var args = new object[2] {42, 42};

    // メソッドを呼び出す(参照渡しとなる引数では、配列内の値が置き換えられる)
    m.Invoke(inst, args);

    // 置き換えられた値を参照する
    Console.WriteLine(args[0]);
    Console.WriteLine(args[1]);
  }
}
MethodInfoでのメソッド呼び出しで参照渡しの引数を参照する
Imports System
Imports System.Reflection

Class C
  Public Sub M(ByVal x As Integer, ByRef y As Integer)
    Console.WriteLine("x = {0}", x)
    Console.WriteLine("y = {0}", y)

    x = 3 ' 値渡し引数の値を変更する (呼び出し元には反映されない)
    y = 3 ' 参照渡し引数の値を変更する (呼び出し元に反映される)
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    Dim inst As Object = Activator.CreateInstance(t)

    Dim m As MethodInfo = t.GetMethod("M")

    ' メソッド呼び出しに使用する引数の配列
    Dim args() As Object = New Object(1) {42, 42}

    ' メソッドを呼び出す(参照渡しとなる引数では、配列内の値が置き換えられる)
    m.Invoke(inst, args)

    ' 置き換えられた値を参照する
    Console.WriteLine(args(0))
    Console.WriteLine(args(1))
  End Sub
End Class
実行結果
x = 42
y = 42
42
3

ジェネリック型のメソッド・ジェネリックメソッド

ジェネリック型のメソッドやジェネリックメソッドを呼び出す場合、引数リストの型はすべて具体的な型に型付けされている必要があります。 型付けされていない状態で呼び出した場合、例外InvalidOperationExceptionがスローされます。 そのため、引数リストにジェネリック型の型パラメータおよびジェネリックメソッドの型パラメータを含む場合は、呼び出す時点で何らかの具体的な型に型付け(置き換え)されている必要があります。

ジェネリック型(Type)ではType.MakeGenericTypeメソッド(関連:§.ジェネリック型の型情報)、ジェネリックメソッド(MethodInfo)ではMethodInfo.MakeGenericMethodメソッドを呼び出すことによって、型パラメータを具体的な型に置き換え、構築された(型付けされた)ジェネリック型・ジェネリックメソッドを取得することができます。

すべての型パラメータが具体的な型に型付けされた状態のMethodInfoでは、通常のMethodInfoと同様にInvokeメソッドで呼び出すことができます。

MethodInfoでジェネリック型のメソッド/ジェネリックメソッドを呼び出す .NET Standard 2.1/.NET Core 2.1
using System;
using System.Reflection;

// 型パラメータTCを取るジェネリック型
class C<TC> {
  // ジェネリック型の型パラメータTCを引数にとるメソッド
  public void M(TC tc) => Console.WriteLine($"M({typeof(TC)} = {tc})");
  // ジェネリック型の型パラメータTCと、メソッドの型パラメータTMを引数にとるジェネリックメソッド
  public void M<TM>(TC tc, TM tm) => Console.WriteLine($"M({typeof(TC)} = {tc}, {typeof(TM)} = {tm})");
}

class Sample {
  static void Main()
  {
    // ジェネリック型定義(C<>)の型情報ではインスタンス作成・メソッド呼び出しはできない
    // MakeGenericTypeメソッドで具体的な型(C<string>など)に型付けする必要がある
    var t = typeof(C<>).MakeGenericType(typeof(string));
    //var t = typeof(C<string>); // あるいは型付けされた状態の型情報を取得する
    var inst = Activator.CreateInstance(t);

    // 引数リストが(TC)のメソッドMを取得する
    var m1 = t.GetMethod("M", types: new[] {
      typeof(string) // 型パラメータTCはMakeGenericTypeでstringに型付けされている
    });

    // メソッドC<string>.M(string)を呼び出す
    m1.Invoke(inst, new object[] {"arg"});

    // 引数リストが(TC, TM)のメソッドMを取得する
    var m2 = t.GetMethod("M",
      genericParameterCount: 1, // 型パラメータが1つのジェネリックメソッド
      types: new[] {
        typeof(string), // 型パラメータTCはMakeGenericTypeでstringに型付けされている
        Type.MakeGenericMethodParameter(0) // 型パラメータTMに対応する、ジェネリックメソッドの0番目の型パラメータを表すTypeを指定
      }
    );
    // MakeGenericMethodを呼び出して、型付けされた状態のMethodInfoを作成する
    var m2g = m2.MakeGenericMethod(
      typeof(int) // 上記の0番目の型パラメータ(TM)を、intに型付けする
    );

    // メソッドC<string>.M<int>(string, int)を呼び出す
    m2g.Invoke(inst, new object[] {"arg", 42});
  }
}
MethodInfoでジェネリック型のメソッド/ジェネリックメソッドを呼び出す .NET Standard 2.1/.NET Core 2.1
Imports System
Imports System.Reflection

' 型パラメータTCを取るジェネリック型
Class C(Of TC)
  ' ジェネリック型の型パラメータTCを引数にとるメソッド
  Public Overloads Sub M(ByVal _tc As TC)
    Console.WriteLine($"M({GetType(TC)} = {_tc})")
  End Sub

  ' ジェネリック型の型パラメータTCと、メソッドの型パラメータを引数TMにとるジェネリックメソッド
  Public Overloads Sub M(Of TM)(ByVal _tc As TC, ByVal _tm As TM)
    Console.WriteLine($"M({GetType(TC)} = {_tc}, {GetType(TM)} = {_tm})")
  End Sub
End Class

Class Sample
  Shared Sub Main()
    ' ジェネリック型定義(C(Of))の型情報ではインスタンス作成・メソッド呼び出しはできない
    ' MakeGenericTypeメソッドで具体的な型(C(Of String)など)に型付けする必要がある
    Dim t As Type = GetType(C(Of)).MakeGenericType(GetType(String))
    ' Dim t As Type = GetType(C(Of String)) ' あるいは型付けされた状態の型情報を取得する
    Dim inst As Object = Activator.CreateInstance(t)

    ' 引数リストが(TC)のメソッドMを取得する
    Dim m1 As MethodInfo = t.GetMethod("M", types := New Type() {
      GetType(String) ' 型パラメータTCはMakeGenericTypeでStringに型付けされている
    })

    ' メソッドC(Of String).M(String)を呼び出す
    m1.Invoke(inst, New Object() {"arg"})

    ' 引数リストが(TC, TM)のメソッドMを取得する
    Dim m2 As MethodInfo = t.GetMethod("M",
      genericParameterCount := 1, ' 型パラメータが1つのジェネリックメソッド
      New Type() {
        GetType(String),                    ' 型パラメータTCはMakeGenericTypeでStringに型付けされている
        Type.MakeGenericMethodParameter(0)  ' 型パラメータTMに対応する、ジェネリックメソッドの0番目の型パラメータを表すTypeを指定
      }
    )
    ' MakeGenericMethodを呼び出して、型付けされた状態のMethodInfoを作成する
    Dim m2g As MethodInfo = m2.MakeGenericMethod(
      GetType(Integer) ' 上記の0番目の型パラメータ(TM)を、Integerに型付けする
    )

    ' メソッドC(Of String).M(Of Integer)(String, Integer)を呼び出す
    m2g.Invoke(inst, New Object() {"arg", 42})
  End Sub
End Class
実行結果
M(System.String = arg)
M(System.String = arg, System.Int32 = 42)

Type.InvokeMemberメソッドを使った呼び出しでは、具体的な型に型付けされないジェネリックメソッドを呼び出すと、例外MissingMethodExceptionがスローされます。

すべての型パラメータが型付けされているジェネリック型(ContainsGenericParametersプロパティfalseのType)における、非ジェネリックなメソッド(型パラメータを取らないメソッド)は、通常のメソッドと同様に呼び出すことができます。

Type.InvokeMemberでジェネリック型のメソッド/ジェネリックメソッドを呼び出す
using System;
using System.Reflection;

// 型パラメータTCを取るジェネリック型
class C<TC> {
  // ジェネリック型の型パラメータTCを引数にとるメソッド
  public void M(TC tc) => Console.WriteLine($"M({typeof(TC)} = {tc})");
  // ジェネリック型の型パラメータTCと、メソッドの型パラメータTMを引数にとるジェネリックメソッド
  public void M<TM>(TC tc, TM tm) => Console.WriteLine($"M({typeof(TC)} = {tc}, {typeof(TM)} = {tm})");
}

class Sample {
  static void Main()
  {
    // ジェネリック型定義(C<>)の型情報ではインスタンス作成・メソッド呼び出しはできない
    // MakeGenericTypeメソッドで具体的な型(C<string>など)に型付けする必要がある
    var t = typeof(C<>).MakeGenericType(typeof(string));
    //var t = typeof(C<string>); // あるいは型付けされた状態の型情報を取得する
    var inst = Activator.CreateInstance(t);

    // メソッドC<string>.M(string)を呼び出す
    t.InvokeMember("M", BindingFlags.InvokeMethod, null, inst, new object[] {"arg"});

    // メソッドC<string>.M<int>(string, int)を呼び出したい
    // (型引数TMは与えられた引数から自動的には型付けされないため、
    // 引数リストと一致するメソッドを見つけられず、結果MissingMethodExceptionをスローする)
    t.InvokeMember("M", BindingFlags.InvokeMethod, null, inst, new object[] {"arg", 42});
  }
}
Type.InvokeMemberでジェネリック型のメソッド/ジェネリックメソッドを呼び出す
Imports System
Imports System.Reflection

' 型パラメータTCを取るジェネリック型
Class C(Of TC)
  ' ジェネリック型の型パラメータTCを引数にとるメソッド
  Public Overloads Sub M(ByVal _tc As TC)
    Console.WriteLine($"M({GetType(TC)} = {_tc})")
  End Sub

  ' ジェネリック型の型パラメータTCと、メソッドの型パラメータを引数TMにとるジェネリックメソッド
  Public Overloads Sub M(Of TM)(ByVal _tc As TC, ByVal _tm As TM)
    Console.WriteLine($"M({GetType(TC)} = {_tc}, {GetType(TM)} = {_tm})")
  End Sub
End Class

Class Sample
  Shared Sub Main()
    ' ジェネリック型定義(C(Of))の型情報ではインスタンス作成・メソッド呼び出しはできない
    ' MakeGenericTypeメソッドで具体的な型(C(Of String)など)に型付けする必要がある
    Dim t As Type = GetType(C(Of)).MakeGenericType(GetType(String))
    ' Dim t As Type = GetType(C(Of String)) ' あるいは型付けされた状態の型情報を取得する
    Dim inst As Object = Activator.CreateInstance(t)

    ' メソッドC(Of String).M(Of String)を呼び出す
    t.InvokeMember("M", BindingFlags.InvokeMethod, Nothing, inst, New Object() {"arg"})

    ' メソッドC(Of String).M(Of Integer)(String, Integer)を呼び出したい
    ' (型引数TMは与えられた引数から自動的には型付けされないため、
    ' 引数リストと一致するメソッドを見つけられず、結果MissingMethodExceptionをスローする)
    t.InvokeMember("M", BindingFlags.InvokeMethod, Nothing, inst, New Object() {"arg", 42})
  End Sub
End Class
実行結果
M(System.String = arg)
Unhandled exception. System.MissingMethodException: Method 'C`1[[System.String, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].M' not found.
   at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
   at System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] args)
   at Sample.Main()

PropertyInfoを使ったプロパティ・インデクサの操作

PropertyInfo.GetValue/SetValueメソッドを使うとプロパティの値の取得・設定を行うことができます。 インデクサ(既定のプロパティ、引数を持つプロパティ)に対する値の設定・取得もこのメソッドで行います。

.NET Framework 4.0以前の場合、PropertyInfo.GetValue/SetValueメソッドでは引数objの他に引数indexも指定する必要があります。 この引数はプロパティがインデクサの場合にインデックスとして使用される値を指定するためのものです。 プロパティがインデクサでない場合は引数indexnull/Nothingを指定します。

.NET Framework 4.5以降では引数indexを必要としないオーバーロードが用意されているため、インデクサではないプロパティではインデックスの指定を省略することができます。

PropertyInfoを使ってプロパティの値を取得・設定する
using System;
using System.Reflection;

class C {
  public int P { get; set; }
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    var inst = Activator.CreateInstance(t);

    // プロパティPのPropertyInfoを取得する
    var p = t.GetProperty("P");

    // プロパティに値42を設定する
    p.SetValue(inst, 42); // .NET Framework 4.5以降の場合
    //p.SetValue(inst, 42, null); // .NET Framework 4.0以前の場合

    // プロパティの値を取得する
    Console.WriteLine("P = {0}", p.GetValue(inst)); // .NET Framework 4.5以降の場合
    //Console.WriteLine("P = {0}", p.GetValue(inst, null)); // .NET Framework 4.0以前の場合
  }
}
PropertyInfoを使ってプロパティの値を取得・設定する
Imports System
Imports System.Reflection

Class C
  Public Property P As Integer
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    Dim inst As Object = Activator.CreateInstance(t)

    ' プロパティPのPropertyInfoを取得する
    Dim p As PropertyInfo = t.GetProperty("P")

    ' プロパティに値42を設定する
    p.SetValue(inst, 42) ' .NET Framework 4.5以降の場合
    'p.SetValue(inst, 42, Nothing) ' .NET Framework 4.0以前の場合

    ' プロパティの値を取得する
    Console.WriteLine("P = {0}", p.GetValue(inst)) ' .NET Framework 4.5以降の場合
    'Console.WriteLine("P = {0}", p.GetValue(inst, Nothing)) ' .NET Framework 4.0以前の場合
  End Sub
End Class
実行結果
P = 42

PropertyInfoを使ってインデクサの値を取得・設定する場合は、インデックスを指定する必要がある以外はプロパティの場合と同様です。

ただし、C#では特に指定しない場合、インデクサの名前はデフォルトでItemとなりますが、IndexerName属性によって変更可能である点に注意してください。

PropertyInfoを使ってインデクサの値を取得・設定する
using System;
using System.Reflection;

class C {
  //[System.Runtime.CompilerServices.IndexerName("Item")]
  public int this[int index] {
    get { return arr[index]; }
    set { arr[index] = value; }
  }

  private int[] arr = new int[2];
}

class Sample {
  static void Main()
  {
    var inst = new C();

    inst[0] = 42;
    inst[1] = 0;

    var t = inst.GetType();

    // インデクサItemのPropertyInfoを取得する
    var p = t.GetProperty("Item");

    // インデクサのインデックス1に値3を設定する
    p.SetValue(inst, 3, new object[] {1});

    // インデクサからインデックス0と1の値を取得する
    Console.WriteLine(p.GetValue(inst, new object[] {0}));
    Console.WriteLine(p.GetValue(inst, new object[] {1}));
  }
}
PropertyInfoを使ってインデクサの値を取得・設定する
Imports System
Imports System.Reflection

Class C
  Public Default Property Item(ByVal index As Integer) As Integer
    Get
      Return arr(index)
    End Get
    Set(ByVal value As Integer)
      arr(index) = value
    End Set
  End Property

  Private arr(1) As Integer
End Class

Class Sample
  Shared Sub Main()
    Dim inst As New C()

    inst(0) = 42
    inst(1) = 0

    Dim t As Type = inst.GetType()

    ' インデクサItemのPropertyInfoを取得する
    Dim p As PropertyInfo = t.GetProperty("Item")

    ' インデクサのインデックス1に値3を設定する
    p.SetValue(inst, 3, New Object() {1})

    ' インデクサからインデックス0と1の値を取得する
    Console.WriteLine(p.GetValue(inst, New Object() {0})) ' inst(0)
    Console.WriteLine(p.GetValue(inst, New Object() {1})) ' inst(1)
  End Sub
End Class
実行結果
42
3

プロパティ・インデクサの参照の結果例外が発生した場合、例外はTargetInvocationExceptionにラップされた上でスローされます。 TargetInvocationExceptionから実際にスローされた例外を取得する方法については§.TargetInvocationException、TargetInvocationExceptionにラップせずにスローさせる方法については§.BindingFlags.DoNotWrapExceptionsを参照してください。

ConstructorInfoを使ったインスタンスの作成

ConstructorInfoを使ってコンストラクタを呼び出し、インスタンスを作成・初期化するには、Invokeメソッドを呼び出します。 呼び出しに際してインスタンスを指定する必要がない点、作成されたインスタンスは戻り値として返される点を除けば、MethodInfo.Invokeメソッドと変わりありません。

ConstructorInfoを使ってコンストラクタを呼び出し、インスタンスを作成する
using System;
using System.Reflection;

class C {
  private string str = "Hello, world!";

  public C()
  {
  }

  public C(string str)
  {
    this.str = str;
  }

  public override string ToString()
  {
    return str;
  }
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    object inst;

    // 引数を持たないコンストラクタのConstructorInfoを取得する
    var c1 = t.GetConstructor(Type.EmptyTypes);

    // コンストラクタを呼び出してインスタンスを作成する
    inst = c1.Invoke(null);

    Console.WriteLine(inst);

    // string型の引数を1つとるコンストラクタのConstructorInfoを取得する
    var c2 = t.GetConstructor(new Type[] {typeof(string)});

    // コンストラクタを呼び出してインスタンスを作成する
    inst = c2.Invoke(new object[] {"Hello, instance!"});

    Console.WriteLine(inst);
  }
}
ConstructorInfoを使ってコンストラクタを呼び出し、インスタンスを作成する
Imports System
Imports System.Reflection

Class C
  Private str As String = "Hello, world!"

  Public Sub New()
  End Sub

  Public Sub New(ByVal str As String)
    Me.str = str
  End Sub

  Public Overrides Function ToString() As String
    Return str
  End Function
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    Dim inst As Object


    ' 引数を持たないコンストラクタのConstructorInfoを取得する
    Dim c1 As ConstructorInfo = t.GetConstructor(Type.EmptyTypes)

    ' コンストラクタを呼び出してインスタンスを作成する
    inst = c1.Invoke(Nothing)

    Console.WriteLine(inst)

    ' String型の引数を1つとるコンストラクタのConstructorInfoを取得する
    Dim c2 As ConstructorInfo = t.GetConstructor(New Type() {GetType(String)})

    ' コンストラクタを呼び出してインスタンスを作成する
    inst = c2.Invoke(New Object() {"Hello, instance!"})

    Console.WriteLine(inst)
  End Sub
End Class
実行結果
Hello, world!
Hello, instance!

コンストラクタ呼び出しの結果例外が発生した場合、例外はTargetInvocationExceptionにラップされた上でスローされます。 TargetInvocationExceptionから実際にスローされた例外を取得する方法については§.TargetInvocationException、TargetInvocationExceptionにラップせずにスローさせる方法については§.BindingFlags.DoNotWrapExceptionsを参照してください。

MethodInfoからデリゲートを作成する (Delegate.CreateDelegate)

Delegate.CreateDelegateメソッドを使うとMethodInfoをデリゲートに変換することができ、デリゲートを介してメソッドの呼び出しを行えるようになります。

CreateDelegateメソッドの引数には、取得したいデリゲートの型を指定します。 デリゲートの型とMethodInfoが表すメソッドのシグネチャは一致している必要があります。 また、インスタンスメソッドの場合は呼び出し対象のインスタンスを指定します。 クラスメソッド(静的メソッド)の場合はインスタンスを指定する必要はありません。 CreateDelegateメソッドの戻り値はDelegateなので、作成したデリゲートを呼び出す場合は適切な型にキャストしてから呼び出す必要があります。

MethodInfoからデリゲートを作成し、デリゲートを介してメソッドを呼び出す
using System;
using System.Reflection;

class C {
  public void M1(string s)
  {
    Console.WriteLine(s);
  }

  public static void M2(string s)
  {
    Console.WriteLine(s);
  }
}

class Sample {
  static void Main()
  {
    var inst = new C();

    var t = typeof(C);

    // インスタンスメソッドM1のMethodInfoからデリゲートを作成する
    var m1 = t.GetMethod("M1");
    var a1 = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), inst, m1);

    a1("Hello, world!"); // 作成したデリゲートを介してメソッドを呼び出す

    // クラスメソッドM2のMethodInfoからデリゲートを作成する
    var m2 = t.GetMethod("M2");
    var a2 = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), m2);

    a2("Hello, world!");
  }
}
MethodInfoからデリゲートを作成し、デリゲートを介してメソッドを呼び出す
Imports System
Imports System.Reflection

Class C
  Public Sub M1(ByVal s As String)
    Console.WriteLine(s)
  End Sub

  Public Shared Sub M2(ByVal s As String)
    Console.WriteLine(s)
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim inst As New C()

    Dim t As Type = GetType(C)

    ' インスタンスメソッドM1のMethodInfoからデリゲートを作成する
    Dim m1 As MethodInfo = t.GetMethod("M1")
    Dim a1 As Action(Of String) = DirectCast([Delegate].CreateDelegate(GetType(Action(Of String)), inst, m1), Action(Of String))

    a1("Hello, world!") ' 作成したデリゲートを介してメソッドを呼び出す

    ' クラスメソッドM2のMethodInfoからデリゲートを作成する
    Dim m2 As MethodInfo = t.GetMethod("M2")
    Dim a2 As Action(Of String) = DirectCast([Delegate].CreateDelegate(GetType(Action(Of String)), m2), Action(Of String))

    a2("Hello, world!")
  End Sub
End Class
実行結果
Hello, world!
Hello, world!

デリゲートからMethodInfoを取得する (Delegate.Method/MulticastDelegate.GetInvocationList)

デリゲート型ではMethodプロパティを参照することで呼び出し対象となるメソッドのMethodInfoを取得することができます。

デリゲートからMethodInfoを取得し、対象となっているメソッドを呼び出す
using System;

class C {
  public void M1() {}
  public static void M2() {}
}

class Sample {
  static void Main()
  {
    var inst = new C();

    // インスタンスメソッドM1のデリゲート
    var a1 = (Action)inst.M1;

    // クラスメソッドM2のデリゲート
    var a2 = (Action)C.M2;

    Console.WriteLine(a1.Method);
    Console.WriteLine(a2.Method);
  }
}
デリゲートからMethodInfoを取得し、対象となっているメソッドを呼び出す
Imports System

Class C
  Public Sub M1()
  End Sub

  Public Shared Sub M2()
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim inst As New C()

    ' インスタンスメソッドM1のデリゲート
    Dim a1 As New Action(AddressOf inst.M1)

    ' クラスメソッドM2のデリゲート
    Dim a2 As New Action(AddressOf C.M2)

    Console.WriteLine(a1.Method)
    Console.WriteLine(a2.Method)
  End Sub
End Class
実行結果
Void M1()
Void M2()

他のデリゲートと連結されたデリゲート(マルチキャストデリゲート)では、GetInvocationListメソッドを使って連結されているすべてのデリゲートを取得してから、各デリゲートのMethodプロパティを参照することで個々のメソッドに対応するMethodInfoを取得できます。

マルチキャストデリゲートから個々のメソッドのMethodInfoを取得する
using System;
using System.Reflection;

class C {
  public void M1() {}
  public static void M2() {}
}

class Sample {
  static void Main()
  {
    var inst = new C();

    var a = (Action)inst.M1;

    // デリゲートの連結
    a += C.M2;

    // 連結されたデリゲートから個々のデリゲートを列挙する
    foreach (var d in a.GetInvocationList()) {
      Console.WriteLine(d.Method);
    }
  }
}
マルチキャストデリゲートから個々のメソッドのMethodInfoを取得する
Imports System

Class C
  Public Sub M1()
  End Sub

  Public Shared Sub M2()
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim inst As New C()

    Dim a As New Action(AddressOf inst.M1)

    ' デリゲートの連結
    a = DirectCast([Delegate].Combine(a, New Action(AddressOf C.M2)), Action)

    ' 連結されたデリゲートから個々のデリゲートを列挙する
    For Each d As [Delegate] In a.GetInvocationList()
      Console.WriteLine(d.Method)
    Next
  End Sub
End Class
実行結果
Void M1()
Void M2()

デリゲートと呼び出されるメソッドに関してはデリゲートの機能 §.呼び出されるメソッド・インスタンスの取得 (Method, Target, GetInvocationList)でも解説しています。

EventInfoを使ったイベントの発行

EventInfoクラスには、直接イベントを発行するメソッドは用意されていません。 そのためイベント発行時の動作と同等の処理を独自に記述する必要があります。 EventInfoクラスからは以下のような手順をとることでイベントの発行を行うことができます。

  1. イベントハンドラのデリゲートを格納しているフィールドのFieldInfoを取得する
  2. 取得したFieldInfoからイベントハンドラを格納しているMulticastDelegateを取得する
  3. MulticastDelegate.GetInvocationListメソッドを使ってイベントハンドラとして割り当てられているメソッドのリスト(MethodInfoの配列)を取得する
  4. 取得したMethodInfoひとつずつに対してメソッドの呼び出しを行う

C#とVBではイベントハンドラを格納しているフィールド名が異なる点に注意が必要です。 例えばイベント名がClickの場合、C#ではフィールドClick、VBではフィールドClickEventにイベントハンドラが格納されます。

上記の処理を具体的に実装すると次のようになります。

EventInfoを使ってイベントの発行を行う
using System;
using System.Reflection;

class C {
  public event EventHandler<EventArgs> E;
}

class Sample {
  static void Handler1(object sender, EventArgs e)
  {
    Console.WriteLine("Handler1");
  }

  static void Handler2(object sender, EventArgs e)
  {
    Console.WriteLine("Handler2");
  }

  static void Main()
  {
    var inst = new C();

    // イベントEにハンドラを割り当てる
    inst.E += Handler1;
    inst.E += Handler2;

    // イベントEのEventInfoを取得する
    var ev = inst.GetType().GetEvent("E");

    // EventInfoを使ってイベントを発行する
    Raise(inst, ev, EventArgs.Empty);
  }

  static void Raise<TEventArgs>(object sender, EventInfo ev, TEventArgs e)
  {
    var f = sender.GetType().GetField(ev.Name, BindingFlags.Instance | BindingFlags.NonPublic);
    var handler = (MulticastDelegate)f.GetValue(sender);

    if (handler == null)
      return;

    foreach (var h in handler.GetInvocationList()) {
      h.Method.Invoke(h.Target, new object[] {sender, e});
    }
  }
}
EventInfoを使ってイベントの発行を行う
Imports System
Imports System.Reflection

Class C
  Public Event E As EventHandler(Of EventArgs)
End Class

Class Sample
  Shared Sub Handler1(ByVal sender As Object, ByVal e As EventArgs)
    Console.WriteLine("Handler1")
  End Sub

  Shared Sub Handler2(ByVal sender As Object, ByVal e As EventArgs)
    Console.WriteLine("Handler2")
  End Sub

  Shared Sub Main()
    Dim inst As New C()

    ' イベントEにハンドラを割り当てる
    AddHandler inst.E, AddressOf Handler1
    AddHandler inst.E, AddressOf Handler2

    ' イベントEのEventInfoを取得する
    Dim ev As EventInfo = inst.GetType().GetEvent("E")

    ' EventInfoを使ってイベントを発行する
    Raise(inst, ev, EventArgs.Empty)
  End Sub

  Shared Sub Raise(Of TEventArgs)(ByVal sender As Object, ByVal ev As EventInfo, ByVal e As TEventArgs)
    Dim f As FieldInfo = sender.GetType().GetField(ev.Name + "Event", BindingFlags.Instance Or BindingFlags.NonPublic)
    Dim handler As MulticastDelegate = DirectCast(f.GetValue(sender), MulticastDelegate)

    If handler Is Nothing Then Return

    For Each h As [Delegate] In handler.GetInvocationList()
      h.Method.Invoke(h.Target, New Object() {sender, e})
    Next
 End Sub
End Class
実行結果
Handler1
Handler2

MulticastDelegate.GetInvocationListメソッドについてはデリゲートの機能 §.呼び出されるメソッド・インスタンスの取得 (Method, Target, GetInvocationList)で解説しています。

静的コンストラクタの呼び出し (Type.TypeInitializer)

Type.TypeInitializerプロパティを参照すると静的コンストラクタ(クラスコンストラクタ)のConstructorInfoを取得することができます。 (BindingFlags.StaticBindingFlags.NonPublicを指定してGetConstructorメソッドを呼び出しても静的コンストラクタを取得することができます)

通常、静的コンストラクタは自動的に一度だけ呼び出されるのみで、ユーザーコードからは呼び出すことはできませんが、TypeInitializerプロパティから取得したConstructorInfoを使うことで静的コンストラクタを任意のタイミングで呼び出すことができます。

Type.TypeInitializerプロパティから静的コンストラクタを取得して呼び出す
using System;
using System.Reflection;

class C {
  // 静的コンストラクタ
  static C()
  {
    Console.WriteLine("initialized");
  }
}

class Sample {
  static void Main()
  {
    var t = typeof(C);

    // 静的コンストラクタのConstructorInfoを取得する
    var cctor = t.TypeInitializer;

    Console.WriteLine(cctor);

    // 静的コンストラクタを呼び出す
    cctor.Invoke(null, null);
  }
}
Type.TypeInitializerプロパティから静的コンストラクタを取得して呼び出す
Imports System
Imports System.Reflection

Class C
  ' 静的コンストラクタ
  Shared Sub New()
    Console.WriteLine("initialized")
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)

    ' 静的コンストラクタのConstructorInfoを取得する
    Dim cctor As ConstructorInfo = t.TypeInitializer

    Console.WriteLine(cctor)

    ' 静的コンストラクタを呼び出す
    cctor.Invoke(Nothing, Nothing)
  End Sub
End Class
実行結果
Void .cctor()
initialized
initialized

実行結果からも分かるとおり、静的コンストラクタが自動的に呼び出された分と、ConstructorInfoを使って呼び出した分の二回分が表示されています。 つまり、ConstructorInfoを使って静的コンストラクタを呼び出したことにより、クラスが再初期化されたことになります。

ほとんどの場合、このような再初期化を行う必要性はありません。 そもそも、静的コンストラクタは通常、型の初期化時に一度だけ呼び出されることを前提とした実装となっていることが多いことから、それ以外のタイミングで静的コンストラクタを呼び出すことはクラスの状態の破壊・不整合を引き起こす可能性があるため、そういった操作の安全性が明確に保証されている場合以外では、すべきではありません。

メンバ呼び出しによる例外の処理

リフレクションによらない通常のメンバ呼び出しでは例外が発生する場合があります。 リフレクションによる呼び出しの場合も、呼び出しの結果として例外が発生する場合があります。 ここではメンバ呼び出しの結果として発生する例外の処理について解説します。

TargetInvocationException

例外TargetInvocationExceptionは、Type.InvokeMemberメソッドやMethodInfo.InvokeメソッドなどMemberInfo派生クラスを介したメンバ呼び出しの際に、呼び出し先で例外が発生した場合にスローされる例外です。

呼び出したメンバで発生した例外は、TargetInvocationExceptionにラップされた上で再スローされます。 実際にメンバがスローした例外は、キャッチした例外のInnerExceptionプロパティを参照することで取得することができます。

Type.InvokeMemberを介した呼び出しでメソッドがスローした例外をキャッチする
using System;
using System.Reflection;

class C {
  public void M() => throw new NotImplementedException();
}

class Sample {
  static void Main()
  {
    var t = typeof(C);
    var inst = Activator.CreateInstance(t);

    try {
      // Type.InvokeMemberを使ってメソッドを呼び出す (呼び出し先で例外が発生する)
      t.InvokeMember("M", BindingFlags.InvokeMethod, null, inst, null);
    }
    // 呼び出したメソッドで例外が発生した場合、TargetInvocationExceptionがスローされる
    catch (TargetInvocationException ex) {
      Console.WriteLine(ex);
      Console.WriteLine();

      // InnerExceptionプロパティを参照してメソッドが実際にスローした例外を取得する
      Console.WriteLine("actual exception type: {0}", ex.InnerException.GetType());
    }
  }
}
Type.InvokeMemberを介した呼び出しでメソッドがスローした例外をキャッチする
Imports System
Imports System.Reflection

Class C
  Public Sub M()
    Throw New NotImplementedException()
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)
    Dim inst As Object = Activator.CreateInstance(t)

    Try
      ' Type.InvokeMemberを使ってメソッドを呼び出す (呼び出し先で例外が発生する)
      t.InvokeMember("M", BindingFlags.InvokeMethod, Nothing, inst, Nothing)

    ' 呼び出したメソッドで例外が発生した場合、TargetInvocationExceptionがスローされる
    Catch ex As TargetInvocationException
      Console.WriteLine(ex)
      Console.WriteLine()

      ' InnerExceptionプロパティを参照してメソッドが実際にスローした例外を取得する
      Console.WriteLine("actual exception type: {0}", ex.InnerException.GetType())
    End Try
  End Sub
End Class
実行結果
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.NotImplementedException: The method or operation is not implemented.
   at C.M()
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
   at System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] args)
   at Sample.Main()

actual exception type: System.NotImplementedException
MethodInfoを介した呼び出しでメソッドがスローした例外をキャッチする
using System;
using System.Reflection;

class C {
  public void M() => throw new NotImplementedException();
}

class Sample {
  static void Main()
  {
    var t = typeof(C);
    var inst = Activator.CreateInstance(t);
    var m = t.GetMethod("M");

    try {
      // MethodInfo.Invokeを使ってメソッドを呼び出す (呼び出し先で例外が発生する)
      m.Invoke(inst, null);
    }
    // 呼び出したメソッドで例外が発生した場合、TargetInvocationExceptionがスローされる
    catch (TargetInvocationException ex) {
      Console.WriteLine(ex);
      Console.WriteLine();

      // InnerExceptionプロパティを参照してメソッドが実際にスローした例外を取得する
      Console.WriteLine("actual exception type: {0}", ex.InnerException.GetType());
    }
  }
}
MethodInfoを介した呼び出しでメソッドがスローした例外をキャッチする
Imports System
Imports System.Reflection

Class C
  Public Sub M()
    Throw New NotImplementedException()
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)
    Dim inst As Object = Activator.CreateInstance(t)
    Dim m As MethodInfo = t.GetMethod("M")

    Try
      ' MethodInfo.Invokeを使ってメソッドを呼び出す (呼び出し先で例外が発生する)
      m.Invoke(inst, Nothing)

    ' 呼び出したメソッドで例外が発生した場合、TargetInvocationExceptionがスローされる
    Catch ex As TargetInvocationException
      Console.WriteLine(ex)
      Console.WriteLine()

      ' InnerExceptionプロパティを参照してメソッドが実際にスローした例外を取得する
      Console.WriteLine("actual exception type: {0}", ex.InnerException.GetType())
    End Try
  End Sub
End Class
実行結果
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.NotImplementedException: The method or operation is not implemented.
   at C.M()
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at Sample.Main()

actual exception type: System.NotImplementedException

MethodInfo.Invokeのほかにも、PropertyInfo.GetValue/SetValueConstructorInfo.Invokeによってプロパティやコンストラクタなどを呼び出した結果例外が発生した場合も、同様にTargetInvocationExceptionがスローされます。

なお、BindingFlags.DoNotWrapExceptionsを指定することにより、例外が発生した場合でもTargetInvocationExceptionにラップさせず、発生した例外そのものをキャッチすることもできます。

BindingFlags.DoNotWrapExceptions

特にオプションを指定せずにType.InvokeMemberメソッドMethodInfo.Invokeメソッドなどによってメンバを呼び出し、呼び出した先で例外が発生した場合、その例外はTargetInvocationExceptionにラップされた上でスローされます。

BindingFlags.DoNotWrapExceptionsを指定することにより、発生した例外をTargetInvocationExceptionにラップしないようにすることができます。 これにより、発生した例外そのものを捕捉できるようになります。 DoNotWrapExceptionsは.NET Standard 2.1/.NET Core 2.1以降で使用することができます。

BindingFlags.DoNotWrapExceptionsを指定して例外をそのままでスローさせる .NET Standard 2.1/.NET Core 2.1
using System;
using System.Reflection;

class C {
  public void M() => throw new NotImplementedException();
}

class Sample {
  static void Main()
  {
    var t = typeof(C);
    var inst = Activator.CreateInstance(t);
    var m = t.GetMethod("M");

    try {
      // MethodInfo.Invokeを使ってメソッドを呼び出す
      // BindingFlags.DoNotWrapExceptionsを指定することにより、
      // 発生した例外をTargetInvocationExceptionにラップさせず、そのままでスローさせる
      m.Invoke(inst, BindingFlags.DoNotWrapExceptions, parameters: null, binder: null, culture: null);
    }
    catch (NotImplementedException ex) {
      Console.WriteLine(ex); // メソッドがスローする例外がそのまま捕捉される
    }
    catch (TargetInvocationException ex) {
      Console.WriteLine(ex); // TargetInvocationExceptionはスローされないので、ここでは捕捉されない
    }
  }
}

BindingFlags.DoNotWrapExceptionsを指定して例外をそのままでスローさせる .NET Standard 2.1/.NET Core 2.1
Imports System
Imports System.Reflection

Class C
  Public Sub M()
    Throw New NotImplementedException()
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim t As Type = GetType(C)
    Dim inst As Object = Activator.CreateInstance(t)
    Dim m As MethodInfo = t.GetMethod("M")

    Try
      ' MethodInfo.Invokeを使ってメソッドを呼び出す
      ' BindingFlags.DoNotWrapExceptionsを指定することにより、
      ' 発生した例外をTargetInvocationExceptionにラップさせず、そのままでスローさせる
      m.Invoke(inst, BindingFlags.DoNotWrapExceptions, parameters := Nothing, binder := Nothing, culture := Nothing)
    Catch ex As NotImplementedException
      Console.WriteLine(ex) ' メソッドがスローする例外がそのまま捕捉される
    Catch ex As TargetInvocationException
      Console.WriteLine(ex) ' TargetInvocationExceptionはスローされないので、ここでは捕捉されない
    End Try
  End Sub
End Class
実行結果
System.NotImplementedException: The method or operation is not implemented.
   at C.M()
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Sample.Main()

動的な生成

リフレクションでは実行時における情報の取得だけでなく、実行時に型情報やメソッドの実装を動的に生成することもできます。 ここではそれらの方法について簡単に解説します。

System.Reflection.Emit

System.Reflection.Emit名前空間TypeBuilderMethodBuilderなどのクラスを使うと型情報やメソッドの実装を動的に生成し、実行することができます。 ただし、メソッドの実装にはILGeneratorクラスを使ってILコード(intermediate language, 中間言語コード)を記述する必要があるため、ILに関する知識が必要となります。

以下の例ではSystem.Reflection.Emit名前空間のクラスを使って以下のようなクラスを動的に生成し、作成したクラスのメソッドを呼び出しています。

生成されるクラスのイメージ
public class HelloWorld {
  public void Print()
  {
    Console.WriteLine("Hello, world!");
  }
}
生成されるクラスのイメージ
Public Class HelloWorld
  Public Sub Print()
    Console.WriteLine("Hello, world!")
  End Sub
End Class
System.Reflection.Emit名前空間のクラスを使って動的にクラスとメソッドを生成する
using System;
using System.Reflection;
using System.Reflection.Emit;

class Sample {
  static void Main()
  {
    // アセンブリ名"DynamicAssmembly"で動的にアセンブリを生成する
    var assmName = new AssemblyName("DynamicAssmembly");

    AssemblyBuilder assm = AppDomain.CurrentDomain.DefineDynamicAssembly(assmName, AssemblyBuilderAccess.Run);

    // 作成したアセンブリ内にモジュールを宣言する
    var module = assm.DefineDynamicModule("DynamicModule");

    // 作成したモジュール内にクラス(object型を基底クラスとした型)を宣言する
    var type = module.DefineType("HelloWorld", TypeAttributes.Public, typeof(object));

    // 作成したクラス内に戻り値がvoid(なし)、引数なしのパブリックメソッドを宣言する
    var method = type.DefineMethod("Print", MethodAttributes.Public, typeof(void), Type.EmptyTypes);

    // メソッドの実装となるILコードを生成する
    var generator = method.GetILGenerator();

    generator.Emit(OpCodes.Ldstr, "Hello, world!");
    generator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new[] {typeof(string)}));
    generator.Emit(OpCodes.Ret);

    // 生成した型情報を使ってインスタンスを作成し、メソッドを呼び出す
    var t = type.CreateType();

    var inst = Activator.CreateInstance(t);

    t.InvokeMember("Print", BindingFlags.InvokeMethod, null, inst, null);
  }
}
System.Reflection.Emit名前空間のクラスを使って動的にクラスとメソッドを生成する
Imports System
Imports System.Reflection
Imports System.Reflection.Emit

Class Sample
  Shared Sub Main()
    ' アセンブリ名"DynamicAssmembly"で動的にアセンブリを生成する
    Dim assmName As New AssemblyName("DynamicAssmembly")

    Dim assm As AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assmName, AssemblyBuilderAccess.Run)

    ' 作成したアセンブリ内にモジュール(VBのModuleではない)を宣言する
    Dim mdl As ModuleBuilder = assm.DefineDynamicModule("DynamicModule")

    ' 作成したモジュール内にクラス(object型を基底クラスとした型)を宣言する
    Dim typ As TypeBuilder = mdl.DefineType("HelloWorld", TypeAttributes.Public, GetType(Object))

    ' 作成したクラス内に戻り値がvoid(なし)、引数なしのパブリックメソッドを宣言する
    Dim method As MethodBuilder = typ.DefineMethod("Print", MethodAttributes.Public, GetType(Void), Type.EmptyTypes)

    ' メソッドの実装となるILコードを生成する
    Dim generator As ILGenerator = method.GetILGenerator()

    generator.Emit(OpCodes.Ldstr, "Hello, world!")
    generator.Emit(OpCodes.Call, GetType(Console).GetMethod("WriteLine", New Type() {GetType(String)}))
    generator.Emit(OpCodes.Ret)

    ' 生成した型情報を使ってインスタンスを作成し、メソッドを呼び出す
    Dim t As Type = typ.CreateType()

    Dim inst As Object = Activator.CreateInstance(t)

    t.InvokeMember("Print", BindingFlags.InvokeMethod, Nothing, inst, Nothing)
  End Sub
End Class
実行結果
Hello, world!

System.Linq.Expressions

System.Linq.Expressions名前空間のクラスを使って式木(expression tree, 式ツリーとも)を作成することによっても実行可能なコードを動的に生成することができます。 .NET Framework 4以降では、式だけでなく条件分岐やループなどのブロック構文も扱うことができるようになっています。 式木では生成したコードをデリゲートに変換(コンパイル)し、任意に呼び出すことができます。

System.Linq.Expressions名前空間のクラスを使って単純な式木を構築して呼び出す
using System;
using System.Linq.Expressions;

class Sample {
  static void Main()
  {
    // 以下のラムダ式と同様の式木を生成する
    // Action<string> a = s => Console.WriteLine(s.ToUpper());

    // 式木に渡されるパラメータ(string型)
    var s = Expression.Parameter(typeof(string), "s");

    // Console.WriteLine(param.ToUpper())に相当する式木を生成する
    var printUpperCase = Expression.Call(
      typeof(Console).GetMethod("WriteLine", new[] {typeof(string)}),
      Expression.Call(s, typeof(string).GetMethod("ToUpper", Type.EmptyTypes))
    );

    // 式木を構成してラムダ式に変換する
    var lambda = Expression.Lambda<Action<string>>(printUpperCase, new[] {s});

    // デリゲートに変換する
    var a = lambda.Compile();

    // 変換したデリゲートに引数を渡して呼び出す
    a("Hello, world!");
  }
}
System.Linq.Expressions名前空間のクラスを使って単純な式木を構築して呼び出す
Option Infer On

Imports System
Imports System.Linq.Expressions

Class Sample
  Shared Sub Main()
    ' 以下のラムダ式と同様の式木を生成する
    'Dim a As Action(Of String) = Sub(s) Console.WriteLine(s.ToUpper())

    ' 式木に渡されるパラメータ(string型)
    Dim s = Expression.Parameter(GetType(String), "s")

    ' Console.WriteLine(param.ToUpper())に相当する式木を生成する
    Dim printUpperCase = Expression.Call(
      GetType(Console).GetMethod("WriteLine", New Type() {GetType(String)}),
      Expression.Call(s, GetType(String).GetMethod("ToUpper", Type.EmptyTypes))
    )

    ' 式木を構成してラムダ式に変換する
    Dim lambda = Expression.Lambda(Of Action(Of String))(printUpperCase, New ParameterExpression() {s})

    ' デリゲートに変換する
    Dim a = lambda.Compile()

    ' 変換したデリゲートに引数を渡して呼び出す
    a("Hello, world!")
  End Sub
End Class
実行結果
Hello, world!
System.Linq.Expressions名前空間のクラスを使って複雑な構文を含む式木を構築して呼び出す
using System;
using System.Linq.Expressions;

class Sample {
  static void Main()
  {
    // 以下のラムダ式と同様の式木を生成する
    /*
    Func<int, int> f = n => {
      int sum = 0;
      for (;;) {
        if (n <= 0)
          break;
        sum += n;
        n -= 1;
      }
      return sum;
    };
    */

    var n = Expression.Parameter(typeof(int), "n");
    var sum = Expression.Parameter(typeof(int), "sum");
    var brk = Expression.Label("break");

    var body = Expression.Block(typeof(int), new[] {sum},
      Expression.Assign(sum, Expression.Constant(0)),
      Expression.Loop(
        Expression.Block(
          Expression.IfThen(
            Expression.LessThanOrEqual(n, Expression.Constant(0)),
            Expression.Break(brk)
          ),
          Expression.AddAssign(sum, n),
          Expression.SubtractAssign(n, Expression.Constant(1))
        ),
        brk
      ),
      sum
    );

    var lambda = Expression.Lambda<Func<int, int>>(body, new[] {n});

    var f = lambda.Compile();

    Console.WriteLine(f(0));
    Console.WriteLine(f(1));
    Console.WriteLine(f(2));
    Console.WriteLine(f(3));
    Console.WriteLine(f(4));
  }
}
System.Linq.Expressions名前空間のクラスを使って複雑な構文を含む式木を構築して呼び出す
Option Infer On

Imports System
Imports System.Linq.Expressions

Class Sample
  Shared Sub Main()
    ' 以下のラムダ式と同様の式木を生成する
    'Dim f As Func(Of Integer, Integer) = Function(n)
    '  Dim sum As Integer = 0
    '  Do
    '    If n <= 0 Then Exit Do
    '    sum += n
    '    n -= 1
    '  Loop
    '  Return sum
    'End Function

    Dim n = Expression.Parameter(GetType(Integer), "n")
    Dim sum = Expression.Parameter(GetType(Integer), "sum")
    Dim brk = Expression.Label("break")

    Dim body = Expression.Block(GetType(Integer), New ParameterExpression() {sum},
      Expression.Assign(sum, Expression.Constant(0)),
      Expression.Loop(
        Expression.Block(
          Expression.IfThen(
            Expression.LessThanOrEqual(n, Expression.Constant(0)),
            Expression.Break(brk)
          ),
          Expression.AddAssign(sum, n),
          Expression.SubtractAssign(n, Expression.Constant(1))
        ),
        brk
      ),
      sum
    )

    Dim lambda = Expression.Lambda(Of Func(Of Integer, Integer))(body, New ParameterExpression() {n})

    Dim f = lambda.Compile()

    Console.WriteLine(f(0))
    Console.WriteLine(f(1))
    Console.WriteLine(f(2))
    Console.WriteLine(f(3))
    Console.WriteLine(f(4))
  End Sub
End Class
実行結果
0
1
3
6
10