C#中如何在三个项目间共享接口?解决依赖循环问题
问题分析与解决方案
你的设计思路完全可行,核心问题出在Core类试图直接创建Data实例——按照依赖倒置原则,高层模块(Core)不应该依赖低层模块(DataLibrary),两者都应该依赖抽象(IDataService)。当前Core不知道Data的存在,自然无法直接实例化,反向引用又会破坏依赖关系,双向引用则导致循环依赖错误。以下是几种可行的解决方案:
方案1:构造函数依赖注入(推荐)
让唯一同时引用Core和Data的控制台项目负责注入具体实现,Core只依赖抽象接口。
修改Core Library代码
public class Core { private readonly IDataService _dataService; // 通过构造函数注入具体实现类 public Core(IDataService dataService) { _dataService = dataService; } public IDataService GetDataService() { return _dataService; } } // 接口保持不变 public interface IDataService { public IUserData UserData { get; set; } } public interface IUserData { public string Name { get; set; } public string LastName { get; set; } }
修改控制台应用代码
class Program { static void Main(string[] args) { // 控制台项目创建具体的Data实例(它引用了DataLibrary) var dataService = new Data(); // 将实例注入Core var core = new Core(dataService); var service = core.GetDataService(); } }
切换数据库时,只需替换DataLibrary项目,然后在控制台里把new Data()换成新的实现类即可,Core和原有代码完全不需要修改。
方案2:服务定位器模式
如果不想用构造注入,可以通过一个全局注册中心获取实例,适合需要灵活切换实现的场景。
在Core Library中添加服务定位器
public static class ServiceLocator { private static readonly Dictionary<Type, object> _registeredServices = new(); public static void Register<T>(T serviceInstance) { _registeredServices[typeof(T)] = serviceInstance; } public static T GetService<T>() { if (_registeredServices.TryGetValue(typeof(T), out var service)) { return (T)service; } throw new InvalidOperationException("Service not registered"); } }
修改控制台应用代码
class Program { static void Main(string[] args) { // 启动时注册Data实例 ServiceLocator.Register<IDataService>(new Data()); var core = new Core(); var dataService = core.GetDataService(); } }
修改Core类的GetDataService方法
public class Core { public IDataService GetDataService() { return ServiceLocator.GetService<IDataService>(); } }
这种方式也能实现解耦,但服务定位器可能会让代码的依赖关系变得不透明,不如构造注入清晰。
方案3:反射动态加载(插件式场景)
如果需要完全动态替换DataLibrary(不需要控制台项目直接引用),可以用反射从DLL中加载实现类。
修改Core类的GetDataService方法
using System.Reflection; public class Core { public IDataService GetDataService() { // 加载DataLibrary的DLL(路径可配置) var assembly = Assembly.LoadFrom("DataLibrary.dll"); // 获取Data类的类型(注意命名空间) var dataType = assembly.GetType("DataLibrary.Data"); // 创建实例并转换为接口 return (IDataService)Activator.CreateInstance(dataType); } }
这种方式适合真正的插件化场景,切换数据库时只需替换DLL文件,不需要修改任何代码,但需要处理反射可能抛出的异常,性能也略低于前两种方案。
内容的提问来源于stack exchange,提问作者Łukasz Łopata
相关产品推荐
相关产品推荐

