C#依赖注入中未实现IDisposable的单例服务所持IDisposable实例的处置疑问
先给你明确结论:你手动创建的ClassB实例的Dispose方法不会被自动调用,原因和解决办法我给你详细拆解清楚:
为什么ClassB的Dispose不会触发?
DI容器只会负责管理它自己创建的服务的生命周期与资源释放。你现在是在注册ClassA时手动new ClassB(),这个ClassB实例完全是你自行创建的,DI容器根本不知道它的存在,自然不会主动调用它的Dispose方法。哪怕ClassA是容器管理的单例,由于ClassA没实现IDisposable,容器也不会对ClassA内部的任何资源做处理。
至于GC,它最终会回收ClassB的内存,但只会调用它的终结器(Finalize方法),不会触发Dispose——这对于需要及时释放的资源(比如文件句柄、数据库连接、网络套接字等)来说风险很高,很可能导致资源泄漏。
两种正确的解决办法
方案1:让ClassA实现IDisposable(最直接的方案)
这就是你提到的Philip Stuyck推荐的方法,让ClassA实现IDisposable接口,在它的Dispose方法里主动调用ClassB的Dispose。这样当DI容器在应用关闭时销毁单例ClassA时,会自动触发ClassA的Dispose,进而正确释放ClassB的资源。
示例代码:
public class ClassA : IDisposable { private readonly ClassB _b; private bool _disposed = false; public ClassA(ClassB b) { _b = b; } // 公开的Dispose方法 public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } // 受保护的处置逻辑,区分托管/非托管资源 protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { // 释放托管资源,调用ClassB的Dispose _b.Dispose(); } // 如果有非托管资源,在这里释放 _disposed = true; } // 终结器,防止忘记调用Dispose时的兜底处理 ~ClassA() { Dispose(false); } }
方案2:把ClassB也交给DI容器管理
如果你不想给ClassA添加IDisposable,可以把ClassB注册为DI服务,让容器来管理它的生命周期。比如注册成单例:
services.AddSingleton<ClassB>(); services.AddSingleton<ClassA>();
这样ClassB是容器创建的,容器知道它实现了IDisposable,当应用关闭时会自动调用它的Dispose方法。ClassA只需要通过构造函数注入ClassB即可,不需要额外处理资源释放。
再明确你的核心疑问
你问“ClassB的Dispose方法会不会被调用?”——答案是不会自动被调用,必须通过上面两种方式之一来确保它被执行,否则只能靠GC的终结器兜底,这并不是正确的资源释放方式。
备注:内容来源于stack exchange,提问作者hyt

