クラスを定義するだけで勝手に使われる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()が出来るだけで、十分便利だと思います。


コメント