[C#][dynamic] プラグイン処理 2 (DLL/C#/Python に対応させてみる)

前回の「プラグイン処理」の続き。
今回は、前回のコードに少し付け足して、様々な種類のプラグインに対応してみよう。
前回は、DLL だけをプラグインとして使えるようにしたが、今回は、それに加えて、C# と Python のプラグインも使えるようにしてみたい。
■ 今回のプラグインの規約
今回のプラグインも、前回同様、以下のような規約ベースで動くものとする。
- 実行中のプログラムがあるフォルダーの下の Plugin と云う名前のフォルダーにある dll ファイルをプラグインのアセンブリ、cs ファイルを C# のプラグイン、py ファイルを Python のプラグインと看做す。
- DLL プラグインと C# プラグインでは、最初の public なクラスをプラグインと見做す。
- プラグインは、必ず string Name() と云う名称を返す public なメソッドを持っている。
- プラグインは、必ず void Run() と云う public なメソッドによって動作する。
■ プラグイン側の実装の例
では、プラグイン側から実装してみよう。
今回用意するのは、以下の三種類だ。
・DLL プラグインの実装の例
DLL プラグインは、クラスライブラリとして作成し、ビルドして "Test.dll" とする。
public なクラスを持ち、その中には、public な Name() メソッドと public な Run() メソッドを持つ。
// DLL プラグイン (クラスライブラリとして作成し、ビルドして "Test.dll" に)
using System;
public class Plugin
{
public string Name()
{
return "DLL Plugin";
}
public void Run()
{
Console.WriteLine("DLL Plugin is running!");
}
}
・C# プラグインの実装の例
C# プラグインは、一つの cs ファイルだ。"Test.cs" として保存する。
public なクラスを持ち、その中には、public な Name() メソッドと public な Run() メソッドを持つ。
// C# プラグイン ("Test.cs" として保存)
using System;
public class Plugin
{
public string Name()
{
return "C# Plugin";
}
public void Run()
{
Console.WriteLine("C# Plugin is running!");
}
}
・Python プラグインの実装の例
Python プラグインは、一つの py ファイルだ。"Test.py" として保存する。
Name() メソッドと Run() メソッドを持つ。
# Python Plugnin (Save as "Test.py".) def Name(): return "Python plugin" def Run(): print "Python plugin is running!\n"
■ プラグインが組み込まれる本体側の実装の例
次に、プラグインが組み込まれる本体側の実装だ。
・IronPython を利用する為の準備
先ず、Python をプラグインとして使えるようにするために、IronPython をインストールしよう。
IronPython は、Visual Studio で NuGet からインストール出来る。

IronPython のインストールが終わると、プロジェクトの参照設定は、次のように IronPython を使う為の参照が追加されている。

・プラグインが組み込まれる本体側の実装の例
では、本体側を実装しよう。
using IronPython.Hosting; // Python プラグインの処理に必要
using Microsoft.CSharp; // C# プラグインの処理に必要
using System;
using System.CodeDom.Compiler; // C# プラグインの処理に必要
using System.IO;
using System.Linq;
using System.Reflection;
class Program
{
// プラグインのフォルダー名
const string pluginFolderName = "Plugin";
static void Main()
{
// プラグインのフォルダーへのフルパス名を取得し、
var pluginFolderName = GetPluginFolderName();
// もしプラグインのフォルダーが在ったら、
if (Directory.Exists(pluginFolderName))
// プラグインのフォルダーの各のプラグインのパス名を使って、プラグインを実行
Directory.GetFiles(pluginFolderName, "*.*").ToList().ForEach(Run);
}
// プラグインのフォルダーへのフルパス名を取得
static string GetPluginFolderName()
{
// 実行中のこのプログラム自体のアセンブリのパスのフォルダーの下の Plugin フォルダーへのフルパスを取得
return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar + pluginFolderName;
}
// プラグインを実行
static void Run(string pluginPath)
{
switch (Path.GetExtension(pluginPath).ToLower()) {
case ".dll": /* DLL の場合 */ RunDll(pluginPath); break;
case ".cs" : /* C# のコードの場合 */ RunCSharp(pluginPath); break;
case ".py": /* Python のコードの場合 */ RunPython(pluginPath); break;
}
}
// DLL プラグインを実行
static void RunDll(string path)
{
// DLL をアセンブリとして読み込む
var assembly = Assembly.LoadFrom(path);
if (assembly != null)
// アセンブリをプラグインとして実行
Run(assembly);
}
// C# プラグインを実行
static void RunCSharp(string pathName)
{
// C# のコードをアセンブリに変換
var assembly = CodeToAssembly(pathName);
if (assembly != null)
// アセンブリに変換されたプラグインを実行
Run(assembly);
}
// Python プラグインを実行
static void RunPython(string pathName)
{
dynamic plugin = Python.CreateRuntime().UseFile(pathName); // Python のコードからランタイムを作成し、
Run(plugin); // それを実行
}
// プラグインを実行
static void Run(Assembly pluginAssembly)
{
// アセンブリを読み込み、その中から public な最初のクラスを取り出す
var pluginType = pluginAssembly.GetExportedTypes().FirstOrDefault(type => type.IsClass);
if (pluginType != null)
// インスタンスを生成し、それをプラグインとして実行
Run(Activator.CreateInstance(pluginType));
}
// プラグインを実行
static void Run(dynamic plugin)
{
Console.WriteLine(plugin.Name()); // プラグインの Name を表示し、
plugin.Run(); // プラグインを Run
}
// C# のコードをコンパイルしてアセンブリに変換
public static Assembly CodeToAssembly(string csharpCode)
{
using (var cscp = new CSharpCodeProvider()) {
// コンパイルした結果のアセンブリを返す
return cscp.CompileAssemblyFromFile(new CompilerParameters { GenerateInMemory = true }, csharpCode).CompiledAssembly;
}
}
}
- DLL プラグインは前回と同じ。
アセンブリとして読み込んで、その中から public な最初のクラスを取り出し、中のメソッドを dynamic に実行する。 - C# プラグインはコンパイルし、メモリ上でアセンブリに変換する。後は DLL プラグインと同じ。
- Python プラグインは、コンパイルせずに、動的にスクリプトとして実行することでメソッドを呼び出す。
dynamic を使うことで、Python のプラグインも同じように実行することができる。
■ プラグイン処理の実行例
では、実行してみよう。
プログラムを実行しているフォルダーの下に Plugin と云う名前のフォルダーを作成し、そこに三つのプラグイン "Test.dll"、"Test.cs"、"Test.py" を置く。

本体プログラムの実行結果は次の通りだ。
DLL Plugin DLL Plugin is running! C# Plugin C# Plugin is running! Python plugin Python plugin is running!
各プラグインが実行された。
■ 今回のまとめ
今回は、前回のプラグイン処理に少し補足を行った。
Visual Basic.NET や F#、IronRuby 等も同様に扱えるのでないだろうか。
ディスカッション
コメント一覧
まだ、コメントがありません