C#において、通常は new ClassName(...) 構文を使用してインスタンスを生成しますが、プラグインシステムやDI(依存性注入)コンテナの実装など、実行時までインスタンス化すべき型が確定しないケースがあります。
このような場合、リフレクション機能の Type.GetConstructor と ConstructorInfo.Invoke を組み合わせることで、引数の型シグネチャを指定して特定のコンストラクタを呼び出し、オブジェクトを生成することが可能です。
目次
特定の引数を持つコンストラクタの動的呼び出し
以下のサンプルコードでは、商品クラス(Product)に対し、int(ID)と string(名前)の2つの引数を持つコンストラクタを特定してインスタンス化する手順を実装しています。
サンプルコード
using System;
using System.Reflection;
public class Program
{
public static void Main()
{
// 1. インスタンス化したいクラスの型情報を取得
// 実際には文字列から Type.GetType("Namespace.Product") 等で取得することもあります
Type targetType = typeof(Product);
Console.WriteLine($"ターゲット型: {targetType.Name}");
// 2. 呼び出したいコンストラクタの引数の型配列を定義
// ここでは public Product(int id, string name) を狙います
Type[] constructorArgsTypes = new Type[]
{
typeof(int),
typeof(string)
};
// 3. 引数の型情報に一致するコンストラクタ情報を取得
ConstructorInfo ctorInfo = targetType.GetConstructor(constructorArgsTypes);
if (ctorInfo != null)
{
// 4. 実引数をobject配列として用意し、コンストラクタを実行(Invoke)
object[] executionArgs = new object[] { 1001, "ゲーミングキーボード" };
// Invokeの戻り値は object 型なので、必要に応じてキャストする
var createdInstance = ctorInfo.Invoke(executionArgs);
// 結果の確認
Console.WriteLine("インスタンス生成に成功しました。");
Console.WriteLine($"生成されたオブジェクト: {createdInstance}");
}
else
{
Console.WriteLine("指定されたシグネチャを持つコンストラクタが見つかりませんでした。");
}
}
}
// サンプル用の商品クラス
public class Product
{
public int Id { get; }
public string Name { get; }
// ターゲットとなるコンストラクタ
public Product(int id, string name)
{
Id = id;
Name = name;
}
// 引数なしコンストラクタ(今回は使用しない)
public Product()
{
Id = 0;
Name = "Unknown";
}
public override string ToString()
{
return $"[Product] ID:{Id}, Name:{Name}";
}
}
解説と技術的なポイント
1. Type.GetConstructor メソッド
クラスには複数のコンストラクタ(オーバーロード)が存在する可能性があります。そのため、GetConstructor メソッドには「どの引数の組み合わせ(シグネチャ)を持つコンストラクタを取得したいか」を示すために、引数の型を表す Type 配列を渡します。
- 引数なしコンストラクタを取得したい場合:
Type.EmptyTypesを渡します。 - 該当するコンストラクタがない場合:
nullが返されます。
2. ConstructorInfo.Invoke メソッド
取得したコンストラクタ情報を使って実際にオブジェクトを生成します。 引数には、コンストラクタの定義順に従って実際の値を格納した object 配列を渡します。戻り値として、生成された新しいインスタンスが返されます。
3. Activator.CreateInstance との違い
簡易的にインスタンスを生成する方法として Activator.CreateInstance も存在します。
- Activator.CreateInstance: 手軽ですが、内部的に適切なコンストラクタを推論して探すため、オーバーロード解決の厳密な制御が難しい場合があります。
- ConstructorInfo.Invoke:
GetConstructorで事前に厳密なシグネチャチェックを行うため、特定のコンストラクタをピンポイントで呼び出したい場合や、パフォーマンスを重視してメタデータをキャッシュする場合に適しています。
