[英]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.