[英]Dynamically load DLL files in C# project - how?
在我的项目中,我需要使用插件。 但是要在我的项目中使用这些,我需要导入插件的引用。 由于我事先不知道项目使用了多少或哪些插件,所以我想在我的项目中动态导入它们。
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
文件?
您需要将对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();
}
}
最后,您可以在不同的程序集中创建任意数量的插件。 它们必须实现合同接口,并且还使用“导出”属性进行修饰,以向 MEF 指示它们应该与任何相应的导入相匹配。 然后将 dll 放入“Plugins”文件夹(此文件夹应与可执行文件位于同一位置)。 这是一个示例插件:
插件.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!");
}
}
}
为了简单起见,我们假设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.AddIn
namespaces ,因为它们是专门为此目的构建的。 但是,如果您不必担心合约的版本控制等问题,依赖注入路线通常会容易得多。
我有一个应用程序,它不仅可以在运行时加载插件,还可以在用户将它们放入文件夹、取出或删除它们时热加载和卸载它们。 所以,当我需要重新编译我的插件时,我不需要重新启动我的应用程序。 在我的例子中,所有插件都派生自插件抽象,因此很容易在.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);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.