如何让plugin.dll调用core-application功能?插件架构交互技术问询
嘿,这在插件架构里属于典型的宿主-插件反向调用问题——插件需要调用宿主(你的core-application)已有的服务对吧?我给你分享几个在.NET环境下最实用的解决方案,从低耦合到快速实现都有:
方案1:定义共享抽象接口(最推荐,符合设计原则)
这是插件架构的标准做法,核心是用依赖倒置原则解耦插件和宿主:
- 先创建一个独立的共享类库(比如命名为
Core.Abstractions),里面只放宿主需要暴露给插件的抽象接口,比如你的Logging功能:
// Core.Abstractions 类库中的代码 public interface ILogger { void Log(string message); }
- 在你的core-application中,让已有的Logging实现类继承这个接口:
// core-application 中的代码 public class StandardLogger : ILogger { // 这里就是你已经配置好的Logging逻辑,比如写入standard-log.txt public void Log(string message) { File.AppendAllText("standard-log.txt", $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}{Environment.NewLine}"); } }
- 插件(plugin1.dll)只需要引用这个
Core.Abstractions类库,不需要直接引用core-application。然后在插件的入口类中,通过构造函数或方法参数接收ILogger实例:
// plugin1.dll 中的代码 public class TimeViewerPlugin { private readonly ILogger _hostLogger; // 通过构造函数注入宿主提供的Logger public TimeViewerPlugin(ILogger hostLogger) { _hostLogger = hostLogger; } public void ShowAndLogCurrentTime() { var currentTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); Console.WriteLine($"当前时间:{currentTime}"); // 直接调用宿主的Logging功能 _hostLogger.Log($"用户查看时间:{currentTime}"); } }
- 最后在core-application加载插件时,把自己的
StandardLogger实例传递给插件:
// core-application 中加载插件的代码 var pluginAssembly = Assembly.LoadFrom(@"modules\plugin1.dll"); var pluginType = pluginAssembly.GetType("TimeViewerPlugin"); // 传入宿主已配置好的Logger实例 var loggerInstance = new StandardLogger(); var pluginInstance = Activator.CreateInstance(pluginType, loggerInstance); // 调用插件的功能方法 var showTimeMethod = pluginType.GetMethod("ShowAndLogCurrentTime"); showTimeMethod.Invoke(pluginInstance, null);
这个方案的优势是完全解耦,插件只依赖抽象,后续宿主升级只要接口不变,插件就不用改动,扩展性极强。
方案2:静态服务入口(快速实现,适合小型项目)
如果不想额外创建共享类库,可以在core-application中暴露一个静态的服务入口,但这种方式耦合度较高,适合快速验证:
- 在core-application中定义一个静态类,暴露已配置好的Logger实例:
// core-application 中的代码 public static class CoreServiceLocator { // 初始化时把已配置好的Logger赋值进来 public static ILogger Logger { get; set; } } // 程序启动时初始化 CoreServiceLocator.Logger = new StandardLogger();
- 插件直接引用core-application的程序集,然后调用静态属性:
// plugin1.dll 中的代码 public void ShowAndLogCurrentTime() { var currentTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); Console.WriteLine($"当前时间:{currentTime}"); CoreServiceLocator.Logger.Log($"用户查看时间:{currentTime}"); }
这种方式简单直接,但插件和宿主强耦合,宿主升级时可能会影响插件,适合小项目或快速原型开发。
方案3:事件通知机制(适合单向通知场景)
如果插件不需要直接调用宿主方法,只是需要触发宿主执行某个操作,可以用事件机制:
- 插件中定义一个事件,当需要记录时间时触发:
// plugin1.dll 中的代码 public class TimeViewerPlugin { // 定义事件,传递需要记录的时间字符串 public event Action<string> OnTimeNeedToLog; public void ShowCurrentTime() { var currentTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); Console.WriteLine($"当前时间:{currentTime}"); // 触发事件,通知宿主记录 OnTimeNeedToLog?.Invoke(currentTime); } }
- core-application加载插件时,订阅这个事件,用自己的Logger处理:
// core-application 中加载插件的代码 var pluginAssembly = Assembly.LoadFrom(@"modules\plugin1.dll"); var pluginType = pluginAssembly.GetType("TimeViewerPlugin"); var pluginInstance = Activator.CreateInstance(pluginType); // 订阅插件的事件,用宿主的Logger处理 var eventInfo = pluginType.GetEvent("OnTimeNeedToLog"); eventInfo.AddEventHandler(pluginInstance, new Action<string>(time => { var logger = new StandardLogger(); logger.Log($"用户查看时间:{time}"); })); // 调用插件方法 var showTimeMethod = pluginType.GetMethod("ShowCurrentTime"); showTimeMethod.Invoke(pluginInstance, null);
这种方式插件完全不知道宿主的Logger存在,只负责触发事件,宿主自己处理逻辑,解耦程度也很高,但只适合单向通知的场景。
内容的提问来源于stack exchange,提问作者Zesa Rex
相关产品推荐
相关产品推荐

