这就是我对"CLR via C#","Effective C#"和其他资源的IDisposable和终结器的理解:
IDisposable用于确定性地清理托管和非托管资源.
负责非托管资源的类(例如文件句柄)应该实现IDisposable并提供终结器以保证它们被清除,即使客户端代码没有在实例上调用Dispose().
仅负责托管资源的类永远不应实现终结器.
如果你有一个终结器,那么你必须实现IDisposable(这允许客户端代码做正确的事情并调用Dispose(),而终结器可以防止泄漏资源,如果他们忘记).
虽然我理解并推理上述所有内容,但有一种情况我认为打破这些规则是有意义的:负责非托管资源的单例类(例如提供对特定文件的单点访问) ).
我认为在单例上使用Dispose()方法总是错误的,因为单例实例应该在应用程序的生命周期中存在,如果任何客户端代码调用Dispose(),那么你就被填充了.但是,您需要一个终结器,以便在卸载应用程序时,终结器可以清理非托管资源.
因此,在我看来,使用没有实现IDisposable的终结器的单例类似乎是一件合理的事情,但这种类型的设计与我所理解的最佳实践相反.
这是一种合理的方法吗?如果没有,为什么不呢?有哪些优越的选择?
我首先提到面向对象的设计模式及其后果并不总是影响每个语言决策,即使在面向对象语言中也是如此.你当然可以找到比一种语言(Smalltalk)更容易实现的经典设计模式,而不是另一种语言(C++).
话虽这么说,我不确定我同意单例实例只应在应用程序结束时处理的前提.我为Singleton(或设计模式:可重用的面向对象软件的元素)阅读的设计模式描述中没有任何内容将此视为此模式的属性.单身人士应该确保在任何一个时刻只存在一个班级的实例; 这并不意味着只要应用程序存在就必须存在.
我有一种感觉,在实践中,许多单身人士确实存在于应用程序的大部分生命中.但是,请考虑使用TCP连接与服务器通信的应用程序,但也可以以断开连接模式存在.连接时,您需要一个单例来维护连接信息和连接状态.断开连接后,您可能希望保留相同的单例 - 或者您可以处置单例.虽然有些人可能认为保持单例更有意义(我甚至可能在其中),但设计模式本身没有任何东西阻止你处理它 - 如果重新连接,单例可以被实例化再次,因为那个时刻没有它的实例存在.
换句话说,您可以创建场景,使单身人士具有IDisposable是合乎逻辑的.
如果非托管资源仅在应用程序退出时释放,则您甚至不必担心终结器,因为无论如何,进程卸载都应为您处理。
如果您有多个应用程序域,并且想处理一个应用程序域的卸载,这可能是一个问题,但是可能您不必担心。
我第二句话说这种设计可能不是正确的选择(并且将使其更难修复,因为随后您发现您确实需要两个实例)在入口点创建对象(或惰性加载包装器对象)并将其通过代码传递到需要的位置,以明确由谁负责将其提供给谁,然后您可以自由地更改决定,只使用一个对其余代码影响很小的决定(使用它所得到的东西)给定)