You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 20:06:33