14

私のプロジェクトでは、プラグインを使用する必要があります。しかし、これらを私のプロジェクトで使用するには、プラグインの参照をインポートする必要があります。プロジェクトで使用するプラグインの数やプラグインが事前にわからないため、プロジェクトに動的にインポートしたいと思います。

    String path = Application.StartupPath;
    string[] pluginFiles = Directory.GetFiles(path, "*.dll");
    ipi = new IPlugin[pluginFiles.Length];
    Assembly asm;

        for (int i = 0; i < pluginFiles.Length; i++)
        {
            string args = pluginFiles[i].Substring(
                pluginFiles[i].LastIndexOf("\\") + 1,
                pluginFiles[i].IndexOf(".dll") -
                pluginFiles[i].LastIndexOf("\\") - 1);

            asm = Assembly.LoadFile(pluginFiles[i]);
            Type[] types = asm.GetTypes();

このコード例では、すべての.dllファイルを検索して文字列リストに入れました。しかし、どうすればこれらすべての.dllファイルをロードできますか?または、.dll実際にインポートせずにこれらのファイルを使用する方法はありますか?

4

3 に答える 3

20

MEF(Managed Extensibility Framework)メソッド:

MEFのインポート/エクスポート機能を利用するプロジェクトにSystem.ComponentModel.Compositionへの参照を追加する必要があります。

まず、ブートストラッパー/ローダー(私の場合は、メインクラスに追加しただけです)。

Program.cs

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using MEFContract;

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            var prgm = new Program();

            // Search the "Plugins" subdirectory for assemblies that match the imports.
            var catalog = new DirectoryCatalog("Plugins");
            using (var container = new CompositionContainer(catalog))
            {
                // Match Imports in "prgm" object with corresponding exports in all catalogs in the container
                container.ComposeParts(prgm);
            }

            prgm.DoStuff();

            Console.Read();
        }

        private void DoStuff()
        {
            foreach (var plugin in Plugins)
                plugin.DoPluginStuff();
        }

        [ImportMany] // This is a signal to the MEF framework to load all matching exported assemblies.
        private IEnumerable<IPlugin> Plugins { get; set; }
    }
}

IPluginインターフェースは、インポートとエクスポートの間のコントラクトです。すべてのプラグインがこのインターフェースを実装します。契約は非常に簡単です:

IPlugin.cs

namespace MEFContract
{
    public interface IPlugin
    {
        void DoPluginStuff();
    }
}

最後に、さまざまなアセンブリで必要な数のプラグインを作成できます。それらはコントラクトインターフェイスを実装する必要があり、対応するインポートと一致する必要があることをMEFに示すために「エクスポート」属性で装飾する必要があります。次に、dllを「プラグイン」フォルダにドロップします(このフォルダは実行可能ファイルと同じ場所にある必要があります)。サンプルプラグインは次のとおりです。

Plugin.cs

using System;
using System.ComponentModel.Composition;
using MEFContract;

namespace Plugin
{
    [Export(typeof(IPlugin))]
    public class Plugin : IPlugin
    {
        public void DoPluginStuff()
        {
            Console.WriteLine("Doing my thing!");
        }
    }
}
于 2012-11-06T15:14:16.230 に答える
4

簡単にするために、のすべての実装にIPluginデフォルトのコンストラクター(publicおよびパラメーターなし)があると仮定しましょう。

そうは言っても、このインターフェースを実装するすべてのタイプを見つけて、それらのインスタンスを作成する必要があります。あなたはある程度正しい方向に進んでいますが、少しのLINQでこれを大幅に単純化できます。

String path = Application.StartupPath;
string[] pluginFiles = Directory.GetFiles(path, "*.dll");


ipi = (
    // From each file in the files.
    from file in pluginFiles
    // Load the assembly.
    let asm = Assembly.LoadFile(file)
    // For every type in the assembly that is visible outside of
    // the assembly.
    from type in asm.GetExportedTypes()
    // Where the type implements the interface.
    where typeof(IPlugin).IsAssignableFrom(type)
    // Create the instance.
    select (IPlugin) Activator.CreateInstance(type)
// Materialize to an array.
).ToArray();

そうは言っても、依存性注入フレームワークを使用したほうがよいかもしれません。これらは通常、コンパイル時に参照されないアセンブリのインターフェイス実装への動的なロードとバインドを可能にします。

また、(私の意見では)少し複雑ですが、名前空間はこの目的のために特別に構築されているため、System.AddIn名前空間を確認することをお勧めします。ただし、コントラクトのバージョン管理などを気にする必要がない場合は、通常、依存性注入ルートの方がはるかに簡単です。

于 2012-11-06T14:44:59.117 に答える
1

プラグインを実行時にロードできるだけでなく、ユーザーがプラグインをフォルダーにドロップしたり、取り出したり、消去したりするときに、プラグインをホットロードおよびアンロードできるアプリケーションがあります。したがって、プラグインを再コンパイルする必要がある場合、アプリケーションを再起動する必要はありません。私の場合、すべてのプラグインはプラグイン抽象から派生しているため、.DLLで簡単に見つけることができます。

これが私のロード方法です:

private static void LoadPlugins(FileInfo file)
{
    try
    {
        Assembly assembly = Assembly.LoadFrom(file.FullName);

        foreach (Type type in assembly.GetTypes())
        {
            if (type.IsSubclassOf(typeof(Plugin)) && type.IsAbstract == false)
            {
                Plugin b = type.InvokeMember(null,
                                            BindingFlags.CreateInstance,
                                            null, null, null) as Plugin;
                plugins.Add(new PluginWrapper(b, file));
                b.Register();
            }
        }
    }
    catch (ReflectionTypeLoadException ex)
    {
        StringBuilder sb = new StringBuilder();
        foreach (Exception exSub in ex.LoaderExceptions)
        {
            sb.AppendLine(exSub.Message);
            if (exSub is FileNotFoundException)
            {
                FileNotFoundException exFileNotFound = exSub as FileNotFoundException;
                if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
                {
                    sb.AppendLine("Fusion Log:");
                    sb.AppendLine(exFileNotFound.FusionLog);
                }
            }
            sb.AppendLine();
        }
        string errorMessage = sb.ToString();
        Log.Error("Plugins Manager", errorMessage);
    }
}
于 2012-11-06T14:47:25.713 に答える