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(托管可扩展性框架)方法:

您需要将System.ComponentModel.Composition的引用添加到使用 MEF 的导入/导出功能的项目中。

首先,引导程序/加载程序(在我的例子中,我只是将它添加到 Main 类)。

程序.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();
    }
}

最后,您可以在不同的程序集中创建任意数量的插件。它们必须实现合同接口,并且还使用“Export”属性进行修饰,以向 MEF 指示它们应该与任何相应的导入相匹配。然后将 dll 放入“插件”文件夹中(该文件夹应与可执行文件位于同一位置)。这是一个示例插件:

插件.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都有默认构造函数(公共且无参数)。

也就是说,您真的想找到实现此接口的所有类型并创建它们的实例。你在某种程度上是在正确的轨道上,但你可以用一点 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.AddInnamespaces,因为它们是专门为此目的而构建的。但是,如果您不必担心合约的版本控制等问题,依赖注入路线通常会容易得多。

于 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 回答