C#で動的にDLLを読込しプラグインを実現してみる

csharp C#

概要

この記事では、C#で動的にDLLの読込を行い、プラグインの機能を実装する方法を紹介します。DLLを増やすだけで動的に機能を拡張したい場合に有効です。

サンプルの構成

サンプルのモジュール構成などを説明します。

モジュール構成

PluginSampleAppは、コンソールアプリで、それ以外はクラスライブラリで構成されます。

クラス構成

クラス構成を示します。SamplePlugin1とSamplePlugin2が、動的に増やす対象のクラスになります。

ファイルの配置構成

ソースコード(PluginSampleApp)

メインのプログラムになります。

IPluginというインターフェースを継承したクラスを各DLLからリフレクションで探しだして、クラスのTypeを取得し、クラスのType情報からインスタンスを生成、インターフェースの形で返却するようになっています。1DLLにIPluginを継承するクラスが1つ、コンストラクタは引数なしのクラスであることを前提としています。

using PluginCommon;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace PluginSampleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // AssemblyからPluginの読み込み
            IPlugin[] plugins = LoadPlugins();

            //Pluginの実行
            foreach(var plugin in plugins)
            {
                Console.WriteLine(plugin.GetMessage());
            }
        }

        static IPlugin[] LoadPlugins()
        {
            //exeと同階層のpluginディレクトリから、dllのリストを取得
            string pluginDirectoryPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\plugin";
            string[] dllFilePathList = Directory.GetFiles(pluginDirectoryPath, "*.dll");

            //dllからIPluginを継承しているクラスを探して、インスタンス化したリストをインターフェースとして返却
            List pluginList = new List();
            foreach(string dllFilePath in dllFilePathList)
            {
                Assembly assembly = Assembly.LoadFrom(dllFilePath);
                Type[] foundTypes = Array.FindAll(assembly.GetTypes(), (t) =>
                {
                    return (t.IsClass && t.IsPublic && !t.IsAbstract && t.GetInterface(typeof(IPlugin).FullName) != null);
                });

                foreach(Type pluginType in foundTypes)
                {
                    pluginList.Add((IPlugin)assembly.CreateInstance(pluginType.FullName));
                }    
            }

            return pluginList.ToArray();
        }
    }
}

ソースコード(PluginCommon)

namespace PluginCommon
{
    public interface IPlugin
    {
        string GetMessage();
    }
}

ソースコード(SamplePlugin1)

using PluginCommon;

namespace SamplePlugin1
{
    public class SamplePlugin1 : IPlugin
    {
        public string GetMessage()
        {
            return "Sample Plugin1 Executed.";
        }
    }
}

ソースコード(SamplePlugin2)

using PluginCommon;

namespace SamplePlugin2
{
    public class SamplePlugin2 : IPlugin
    {
        public string GetMessage()
        {
            return "Sample Plugin2 Executed.";
        }
    }
}

出力結果

プログラムを実行すると、pluginディレクトリ以下に格納した、DLLが返却している文字列が表示されていることがわかります。

コメント