C# Reflectionで派生クラスを自動登録する(Plugin型アーキテクチャ)

コンピュータ

クラスを定義するだけで勝手に使われるPluginのような振る舞いのサンプルコードを作成しました。

abstract class BaseClass()
{
	abstract public void Apply();
}

class DerivedClass1 : BaseClass
{
	override public void Apply()
	{
		Console.WriteLine("DerivedClass1.Apply()");
	}	
}

class DerivedClass2 : BaseClass
{
	override public void Apply()
	{
		Console.WriteLine("DerivedClass2.Apply()");
	}		
}

class Program
{
	// 手動
	public static void Method1()
	{
		List<BaseClass> objs = [];

		objs.Add(new DerivedClass1());
		objs.Add(new DerivedClass2());
		
		objs.ForEach(x => x.Apply());
		// 結果
		// DerivedClass1.Apply()
		// DerivedClass2.Apply()
	}
	// 自動
	public static void Method2()
	{
		List<BaseClass> objs = [];
		
		// ここでリフレクションを使い、
		// BaseClassの派生クラスをリストアップ、
		// 生成しobjsに追加

		var types = typeof(BaseClass).Assembly	// BaseClass が定義されている DLL / EXE
			.GetTypes()	// アセンブリ内に定義されているすべての型(Type)を取得
			.Where(t =>
				typeof(BaseClass).IsAssignableFrom(t) && // tがBaseClassまたはその派生クラスならtrue
				!t.IsAbstract);	// tがabstractクラスではないこと(インスタンス化可能)

		foreach (var type in types)
		{
			var obj = (BaseClass)Activator.CreateInstance(type)!; // typeで指定された型のインスタンスを生成し、BaseClass型にキャスト
			objs.Add(obj);
		}

		objs.ForEach(x => x.Apply());
		// 結果
		// DerivedClass1.Apply()
		// DerivedClass2.Apply()
	}
	public static void Main()
	{
		// Method1();

		Method2();
	}
}

Method1() と Method2() はどちらも、
オブジェクトを生成してコレクションに追加し、
そのコレクションの各要素に対して順番に Apply() メソッドを実行しています。

Method1() では、生成するオブジェクトをコードに直接記述しています。

この方法の場合、派生クラスが増えるたびに

objs.Add(new DerivedClassX());

というコードを追加していく必要があります。

一方、Method2() ではリフレクションを使用して
BaseClass の派生クラスを探し出し、オブジェクトを生成しています。

この方法を使うと、DerivedClassX のような派生クラスを新しく定義するだけで、
自動的にオブジェクトが生成されるコードが実行されるようになります。

つまり、クラスを追加するだけで機能を拡張できる仕組みになります。

いわゆる Plugin と呼ばれる仕組みは、
このようなリフレクションの仕組みを活用して実現されています。

本格的なプラグイン機構では、DerivedClassX のようなクラスを
DLL として別ファイルに分離し、アプリケーション本体から動的に読み込みます。

その DLL 内に定義されたクラスをリフレクションで取得し、
インスタンスを生成することで、アプリケーションの機能を拡張します。

このような構造にすることで、アプリケーション本体のコードを変更せずに、
DLL を追加するだけで新しい機能を利用できるようになります。

つまり、

  • 本体プログラム
  • プラグイン DLL

という構成にすることで、後から機能を追加できる拡張性の高い仕組みを実現できます。

本稿では、DLL化までは扱いません。

そこまで、しなくとも

ある程度クラスの数が増えてくると、Method2()が出来るだけで、十分便利だと思います。

コメント