C#で動的にマネージ・ライブラリを読み出す方法の模索
C#でプラグインを追加できるプログラムを作ろうとすると、動的にライブラリを読み込む必要性が出てくると思います。
その方法を考えてみました。
メモ書き的に書くのでわかりやすくないかもしれません。需要があればわかりやすく書くのでコメントしてください。
ちなみにC#2.0にこだわっているのでこんなやり方になっています。たしかにdynamicもあるのですが、うーん…と思うのでこうしました。
追記 AppDomainを使うと動的にUnloadも出来るそうです。機会があればまとめます。
構成
全体で3つの要素に分かれています
- メイン・プログラム
- ライブラリを動的に読み出す
- インターフェース
- ライブラリの形式を規定
- ライブラリ
- インターフェースに則ったクラス
バイナリですが、3つ別々にコンパイルするか、本体プログラムとインターフェースをまとめて、ライブラリを別に作るという方法があります。それは各自におまかせします。
方針としては、インターフェースに則ったライブラリクラスの型を取得し、コンストラクタによってインスタンスを生成します。そのオブジェクトをインターフェースを介してメソッドを呼び出すことで、ライブラリの役割を果たす、というものです。
インターフェース
インターフェースはライブラリの形式を規定しています。
簡単に、こんなインターフェースにしましょう
public interface MyInterface { void Something(); }
このインターフェースを含むアセンブリをコンパイルして用意しておいてください。
ちなみに、インターフェースである必要性はありません。クラス、抽象クラスでも構いません。
ライブラリ
先程作っておいた、インターフェースを含むアセンブリを参照に追加しておいてください。
すると、MyInterfaceを継承したクラスを作れるはずです。そこで、継承したクラスを作ってみましょう!
public class MyLibrary : MyInterface { public MyLibrary() { } public void Something() { Console.WriteLine("MyLibrary!"); } }
メイン・プログラム
メイン・プログラムではインターフェースを参照に加えておきましょう。当然ですがインターフェースと同じアセンブリとしてコンパイルしたのならそのままで大丈夫です。
さて、ライブラリを動的に読み出す部分ですが、もしクラス名が完全に指定されている場合であれば
MyInterface obj = MyLibrary.CreateInstance("MyLibrary.MyLibrary") as MyInterface; obj.Something();
とすることが出来ます。しかし、これではライブラリ作成者側にクラス名を名付ける自由度奪ってしまうので、好ましくありません。
そこで、MyInterfaceを継承するクラスを抜き出してくるということをしましょう
Assembly MyLibrary = Assembly.LoadFrom("MyLibrary.dll"); foreach (Type t in MyLibrary.GetTypes()) { foreach (Type i in t.GetInterfaces()) { if (i == typeof(MyInterface)) { Console.WriteLine(t.FullName); FooBarInterface obj = Activator.CreateInstance(t) as FooBarInterface; obj.Something(); Console.WriteLine(); break; } } }
こうすることで、アセンブリ内の(すべての)MyInterfaceを継承するクラスのインスタンスを作成し、Something()を実行するということができるようになりました。
これよりも、もっといい方があるという場合は教えていただけますでしょうか?