このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
プラグインを動的に読み込む所から考えてみよう。
現在 Dispatcher.cpp では、#using ディレクティブを使い、
<HelloSaver.dll> を直接参照しているため、
以下のコード断片だけでインスタンスを作成できる。
// スクリーンセーバーを作成
HelloSaver saver;
これを動的に読み込むように切り替えるためには、
まず、HelloSaver.dll への依存を外さなければならない。
そうなると、「HelloSaver」のように、
プラグイン実装のクラス型を直接使うことはできないため、
リフレクションを使ってアクセスすることになる。
まず最初に、アセンブリをロードし、
ISaver を実装している型を取得する。
この手順は以下の通り。
1. Assembly::LoadFrom を使ってアセンブリを読み込む
2. Assembly::GetType を使って名前で型を取得するか、
Assembly::GetTypes を使って全ての型を得る。
3. Type が ISaver を実装し、利用可能かどうかを調べる。
コードで書くとこんな感じ。
using namespace System;
using namespace System::Collections::Generic;
using namespace System::Reflection;
using namespace Loafer::ScreenSaver::Plugins;
IList<Type ^> ^LoadScreenSaverTypes(String ^assemblyPath) {
// ISaver の Type を取得
Type ^interfaceType = ISaver::typeid;
// 戻り値用の Type の List を用意
IList<Type ^> ^types = gcnew List<Type ^>();
// アセンブリを AppDomain に動的ロード
Assembly ^plugin = Assembly::LoadFrom(assemblyPath);
// アセンブリに含まれる型を列挙
for each (Type ^type in plugin->GetTypes()) {
// public で抽象ではなく ISaver を実装し
// デフォルトコンストラクタを持つ
if (type->IsPublic && !type->IsAbstract
&& interfaceType->IsAssignableFrom(type)
&& type->GetConstructor(Type::EmptyTypes) != nullptr) {
types->Add(type);
}
}
return types;
}
IList<Type ^> ^ は Generics の構文である。
その構文は C++ のテンプレートとほぼ同じである。
C++/CLI なので、IList も Type もハンドルとなる。
Assembly::GetTypes は配列を返す。
マネージ配列は IEnumerable を実装しているため、
C++/CLI で追加された for each 構文を使って列挙可能だ。
Type ^ が取得できたら、型の詳細を調べる。
Assembly::GetTypes は全ての型を返すので、
まず、public で abstract ではないクラスに限定し、
IsAssignableFrom で、ISaver と互換性があるか調べる。
最後に GetConstructor に Type::EmptyTypes を渡すことで、
引数なしの public コンストラクタがあるかどうか調べる。
これらをすべて満たしていれば、インスタンスを作成し、
ISaver にキャストできることが期待される型となる。
型 (Type ^) が取得できたら、
型からインスタンスを生成して ISaver にキャストする。
この手順は以下の通り。
4. Type::GetConstructor か Activator::CreateInstance で、
Type が示すクラスのインスタンスを作成する。
5. 戻り値を ISaver にキャストする。
これはそれほど難しくない。
ISaver ^ CreateScreenSaver(Type ^saverType) {
// インスタンスを作成し
Object ^instance = Activator::CreateInstance(saverType);
// キャストして返す
return safe_cast<ISaver ^>(instance);
}
インスタンスの生成で最も手軽なのは、
専用のユーティリティクラスである Activator を使い、
Activator::CreateInstance を呼び出すことだ。
他にも、Type::GetConstructor 等で取得できる、
ConstructorInfo を使うこともできる。
ConstructorInfo ^ctor = saverType->GetConstructor(Type::EmptyTypes);
Object ^instance = ctor->Invoke(nullptr);
型からインスタンスを作成する方法はいくつもあるのだ。