如何在运行时加载第三方实现

问题描述 投票:1回答:2

我正在制作一个.Net应用程序来管理和安装mod。该应用程序本身不应该能够为任何特定的游戏安装mod,但是应该可以调用第三方扩展来这样做。

比方说,我的国防部经理希望实现给定接口:

interface IGameManager {
    // Deploy a modding configuration to the targeted game
    void Deploy();
    // Remove all managed mods from the targeted game
    void Purge();
    // ...
}

还有其他人,使用不同的代码库,实现IGameManager来管理特定的游戏:

class MinecraftManager: IGameManager {
    // ...
}

然后这个人编译它,发布它,每个人都可以简单地将此扩展提供给主模块管理器,以便它可以为目标游戏管理其模块。

但是如何?我的应用程序是否可以在运行时安全地加载和使用此类第三方实现?以及如何促进第三方扩展的制作(例如,提供一个可在其上构建但更优雅且易于维护的界面)?

编辑1:MinecraftManager签名中的语法无效

c# .net
2个回答
4
投票

您实际上是在设计插件系统。您可以重用许多实现,但是总体思路是:

  1. 您需要您的管理员才能发现扩展名。有许多方法可以做到这一点,但是最简单和最常用的方法是将扩展程序集放置在文件系统中一个众所周知的目录下。然后,您的经理可以通过枚举文件来枚举该文件夹中的程序集(或者,如果您希望每个扩展名都有自己的子文件夹,请枚举子文件夹)

  2. 加载程序集。为此,您将使用Assembly.Load..方法之一。由于无法卸载程序集,因此您可能希望首先仅装载程序集以进行反射,并且一旦确定程序集有效,就可以将其装载到ApplicationDomain中以使用它。

  3. 使用relfection枚举刚加载的程序集的所有类,并找到实现正确接口(IGameManager)的那些类。另外,您可以要求扩展名包含一个已知名称的“入口点”类,然后按名称查找该类(使用反射)。

  4. 创建类的实例并使用它(也许也将其保存在已加载扩展的集合中)]] >>

    关于扩展程序必须实现的接口:您应该将接口(和任何其他支持接口)放在单独的程序集中。该程序集应仅包含接口,而不应包含任何实现。然后,您可以发布程序集。一旦发布,界面就永远不会改变。

  5. 如果需要添加功能,则应创建一个新界面。这样,管理器的旧版本将与扩展的较新版本一起使用(该扩展也旨在实现新功能)。您的经理还可以确定扩展实现的接口并采取相应的措施(从而保持兼容性)。如果新功能是强制性的,则您的经理应丢弃所有未实现两个接口的扩展。

[我刚刚发现dotNet 4已经具有可扩展性框架(Documentations here),它比使用Assembly.Load()更干净,更安全。

如果有人遇到相同的问题,这是我从给定目录加载插件的代码段:

// Where T is the type you want to retrieve from the assemblies
private static IEnumerable<Lazy<T>> LoadExternalAssemblyFromPath<T>(string path, string pattern) {
    CreateDirectoryIfDoesntExist(path);
    AggregateCatalog catalog = new AggregateCatalog();
    catalog.Catalogs.Add(new DirectoryCatalog(path, pattern));

    CompositionContainer container = new CompositionContainer(catalog);
    return container.GetExports<T>();
}

// Usage:
LoadExternalAssemblyFromPath("C:/path/to/plugins", "*.dll");

关于这种插件的实现,Kouvarakis在此问题上的解决方案是正确的。


0
投票

[我刚刚发现dotNet 4已经具有可扩展性框架(Documentations here),它比使用Assembly.Load()更干净,更安全。

© www.soinside.com 2019 - 2024. All rights reserved.