動的クラスロード
動的クラスロードのためのインターフェース
組み込みクラスをRiceに登録する唯一の方法は、前章で説明したRiceManagerクラスの静的なメンバ関数であるAddBuiltIn()メンバ関数です。
public static void AddBuiltIn(string typename, PlainProtoTypeGetter pptg)
このメンバ関数にクラス名とPlainProtoTypeGetter型のデリゲートを渡して呼び出せば、組み込みクラスを登録できます。
つまり、動的クラスロードとは、動的なAddBuiltIn()メンバ関数の呼び出しです。
Riceは動的クラスロードのためのインターフェースとして、IEmbedPrototypeインターフェースとRiceManagerクラスの静的なメンバ関数であるRegisterClasses()メンバ関数を提供しています。
Riceを利用するアプリケーションが独自のインターフェースを用意することもできますが、統一されたインターフェースを用いることで、アプリケーション間で組み込みクラスを再利用することが出来ます。
IEmbedPrototypeインターフェース
IEmbedPrototypeインターフェイスは、組み込みクラスを含むdllファイルが動的にロードされるときに呼び出されるインターフェイスです。
以下に、IEmbedPrototypeインターフェースを示します。
public interface IEmbedPrototype { void Embed(string path);}
IEmbedPrototypeインターフェースは、Riceソースのrice.csで定義されており、唯一のメンバ関数であるEmbed()メンバ関数を持ちます。
Embed()メンバ関数は引数として文字列を取ります。これは組み込まれるdllファイルの絶対パスが渡されることを想定しています。
下記で説明しますが、RegisterClasses()メンバ関数は動的にロードしたdllファイル内にある、IEmbedPrototypeインターフェースを持つクラスを探索し、そのEmbed()メンバ関数を呼び出します。
つまり、dllファイル内にIEmbedPrototypeインターフェースを持つクラスを定義して、そのEmbed()メンバ関数でAddBuiltIn()メンバ関数を呼び出せば、動的なクラスロードが実現できます。
例として、RtemplateクラスのためのIEmbedPrototypeインターフェースを実装してみましょう。
RtemplateEmbedderが動的なロードのためのクラスです。このクラスをRtemplateと同じプロジェクトに置いて、プロジェクトをビルドすれば動的ロード可能なdllファイルが作成されます。
1: | using Rice; |
2: | |
3: | public class RtemplateEmbedder : IEmbedPrototype { |
4: | public void Embed(string path) { |
5: | RiceManager.AddBuiltIn(Rtemplate.TYPENAME, Rtemplate.InstanceGetter); |
6: | } |
7: | } |
RegisterClasses()メンバ関数
RegisterClasses()メンバ関数は、指定されたディレクトリ内にある全ての組み込みクラスのdllファイルを探索して、クラスを動的にロードするためのメンバ関数です。
RegisterClasses()メンバ関数は引数として文字列を一つ取ります。この引数に適切なディレクトリの絶対パスを渡して呼び出してください。
組み込みクラスのdllファイルを発見した場合は、IEmbedPrototypeインターフェースのEmbed()メンバ関数を呼び出します。
探索は再帰的に行われます。つまり、引数で指定したディレクトリだけでなく、その全ての子孫ディレクトリに対しても探索が行われます。
以下に、RegisterClasses()メンバ関数の実装を示します。
1: | //指定ディレクトリ内にある組み込みクラスの一括登録 |
2: | public static void RegisterClasses(string directoryPath) { |
3: | DirectoryInfo dllsInfo = new DirectoryInfo(directoryPath); |
4: | if(dllsInfo.Exists) { |
5: | _recursiveRegistration(dllsInfo); |
6: | } |
7: | } |
8: | |
9: | //組み込みクラスのdllファイルの再帰探索と組み込み。 |
10: | private static void _recursiveRegistration(DirectoryInfo currentDirectory) { |
11: | foreach(FileInfo childFile in currentDirectory.GetFiles("Rtype.*.dll")) { |
12: | Assembly asm = Assembly.LoadFrom(childFile.FullName); |
13: | foreach(Type t in asm.GetTypes()) { |
14: | IEmbedPrototype e = null; |
15: | try { |
16: | e = Activator.CreateInstance(t) as IEmbedPrototype; |
17: | } |
18: | catch { |
19: | continue; |
20: | } |
21: | if(e != null) |
22: | e.Embed(childFile.FullName); |
23: | } |
24: | } |
25: | foreach(DirectoryInfo childDirectory in currentDirectory.GetDirectories()) { |
26: | _recursiveRegistration(childDirectory); |
27: | } |
28: | } |
埋め込みクラスdllファイルの検出は、ファイル名に基づいています。
RegisterClasses()メンバ関数を使って組み込みクラスをロードする場合は、組み込みクラスのdllファイル名が、ファイル名のパターン"Rtype.*.dll"に合致する必要があります。
例えば、
datetimeクラス用のdllファイルなら、"Rtype.Datetime.dll"。
regex(正規表現)クラス用のdllファイルなら、"Rtype.Regex.dll"。
これにより、組み込みクラスのdllファイルと他のdllファイルが混在しても、組み込みクラスのdllファイルだけが適切にロードされます。