两种返回类实例方式的差异及服务提供者实现意义的技术咨询
两种返回类实例方式的差异及服务提供者实现意义的技术咨询
兄弟,我太懂你现在的疑惑了——直接new一个实例看起来简单又直接,为啥要绕这么大弯用依赖注入的服务提供者那套?咱们来好好拆解下这两种方式的差异,以及后者到底有啥实际价值。
先把你的代码贴出来方便对照:
public class UdpFactory : IUdpFactory { public int Port; public ObservableCollection<string> Log; public IUdpListener CreateUdpListener(int port, ObservableCollection<string> lgm) { var services = new ServiceCollection(); var isServer = Environment.GetEnvironmentVariable("DefineConstants")?.Contains("Server"); if (isServer.HasValue && isServer.Value) { return new UDPServer(); //services.AddTransient<IUdpListener, UDPServer>(); } else { return new UDPServerClnt(); //services.AddTransient<IUdpListener, UDPServerClnt>(); } //var serviceProvider = services.BuildServiceProvider(); //return serviceProvider.GetRequiredService<IUdpListener>(); } }
先说说你现在直接new实例的方式的局限
- 耦合度拉满:你的
UdpFactory现在直接绑定了UDPServer和UDPServerClnt的具体实现,以后要是想换个新的IUdpListener实现(比如UdpAdvancedServer),你必须修改UdpFactory里的代码,完全违反了开闭原则——对扩展开放,对修改关闭。 - 手动处理依赖是噩梦:如果哪天你的
UDPServer构造函数需要新增依赖(比如ILogger<UDPServer>、IConfiguration或者其他业务服务),你所有直接new UDPServer()的地方都得手动传递这些依赖项,要是依赖层级深了,改起来能让你头大。 - 生命周期全靠自己硬写:要是你以后想把
IUdpListener改成单例实例(整个应用里只存在一个)或者作用域实例(比如每个请求一个),直接new的话你得自己写一堆逻辑去管理实例的创建和销毁,很容易写出隐蔽的bug。
再看注释里的服务提供者(DI容器)方式的核心价值
- 彻底解耦:
UdpFactory只需要依赖IUdpListener接口和DI容器,具体的实现类是在ServiceCollection里注册的。以后要换实现,只需要改注册代码(比如把AddTransient<IUdpListener, UDPServer>()换成AddTransient<IUdpListener, UdpAdvancedServer>()),完全不用动UdpFactory的核心逻辑。 - 自动注入依赖省老事:DI容器会自动处理
IUdpListener实现类的所有依赖项。比如UDPServer需要ILogger<UDPServer>,你只需要在容器里注册过ILogger,容器就会自动创建并注入这个依赖,你根本不用手动去newLogger。 - 统一生命周期管理:通过
AddTransient(每次获取都new一个新的)、AddScoped(每个作用域一个实例)、AddSingleton(全局单例)这几个方法,你可以轻松控制实例的生命周期,容器会帮你处理实例的创建、复用和销毁,不用自己写一堆冗余的管理逻辑。 - 测试起来巨方便:做单元测试的时候,你可以直接在
ServiceCollection里注册IUdpListener的Mock实现(比如用Moq框架创建的Mock<IUdpListener>),然后通过容器获取实例,完全不用修改UdpFactory的代码,测试的隔离性和灵活性直接拉满。
回到你的代码场景
现在你的UDPServer和UDPServerClnt可能没有额外依赖,所以直接new看起来问题不大,但这是典型的“短视”写法——业务一复杂,上面说的那些局限就会逐个暴露出来。而注释里的DI容器方式,是在为未来的复杂度提前铺路,让代码更健壮、更易维护。
内容来源于stack exchange
相关产品推荐
相关产品推荐

