2009-01-21 38 views
14

这是我了解了IDisposable和终结从“通过C#CLR”,“有效的C#”和其他资源:辛格尔顿与终结,但不是IDisposable的

  • IDisposable的是清理托管和非托管资源确定性。
  • 负责非托管资源(例如文件句柄)的类应该实现IDisposable并提供一个终结器,以确保即使客户端代码不调用实例上的Dispose(),它们也会被清除。
  • 只负责管理资源的类不应该实现终结器。
  • 如果你有一个终结器,那么你必须实现IDisposable(这允许客户端代码做正确的事,并调用Dispose(),而终结器可以防止漏掉的资源,如果他们忘记)。

虽然我理解推理,并与所有上述的同意,有一个场景,我觉得很有道理,打破这些规则:一个单身类,负责非托管资源(如提供单访问特定文件的点)。

我相信在单例中有一个Dispose()方法总是错误的,因为单例实例应该活在应用程序的整个生命周期中,并且如果任何客户端代码调用Dispose(),那么你就塞满了。但是,您需要一个终结器,以便卸载该应用程序时,终结器可以清理非托管资源。

因此,使用不带实现IDisposable的终结器的单例类似乎是合理的事情,但这种类型的设计与我所了解的最佳实践相反。

这是一个合理的方法吗?如果不是的话,为什么不,优越的选择是什么?

回答

3

如果非托管资源仅在应用程序退出时释放,您甚至不需要担心终结器,因为无论如何,进程卸载都应该为您处理。

如果您有多个应用程序域,并且您想要处理应用程序域卸载,这可能是一个可能的问题,但可能不需要关心。

我第二次说这个设计可能不是正确的做法(并且会使它更难修复,随后你发现你实际上需要两个实例) 创建对象(或者延迟加载包装器对象)并将其传递到需要的地方,以明确谁负责将其提供给谁,然后您可以自由地更改决定,只使用一个对代码其余部分影响不大的决定(它使用它被赋予)

4

我首先提到面向对象的设计模式及其后果并不总是影响每一种语言决策,即使在面向对象的语言中也是如此。您当然可以找到经典的设计模式,这些模式更容易用一种语言(Smalltalk)而不是另一种语言(C++)来实现。

这就是说,我不确定我是否同意单一实例只应用于应用程序末尾的前提。我在Singleton(或Design Patterns: Elements of reusable Object-Oriented Software)的设计模式描述中没有提到这是此模式的一个属性。单身人士应确保在任何时刻只有一个班级实例存在;这并不意味着只要应用程序存在就必须存在。

我有一种感觉,在实践中,许多单身人士确实存在于应用程序的大部分生命中。但是,考虑使用TCP连接与服务器通信的应用程序,但也可以以断开模式存在。连接时,您希望单身人员保持连接信息和连接状态。一旦断开连接,你可能想要保持同一个单身人士 - 或者你可以处理单身人士。虽然有些人可能会争辩说保持单身人士更有意义(我甚至可能是其中的一员),但在设计模式本身中没有任何东西可以阻止您处理它 - 如果重新创建连接,则可以实例化单身人士再一次,因为在那个时候没有任何实例存在。

换句话说,您可以创建情景,让单身人士拥有IDisposable。

+1

事实是,如果你可以想象你的对象在应用程序的生命周期中被替换的原因,该对象不能合法地成为单例。至于为什么......说我抓住Singleton并将它传递给需要它的东西(原因,你知道,依赖注入是一件好事)。然后某个地方决定取代它。现在有*两个单身人士*(我已经过去的旧单身人士和新单身人士)。这种可能性违反了Singleton的整个定义 - 也就是说,代码总是只能看到一个实例。 – cHao 2012-01-30 22:23:04

+0

所以,首先,圣洁的老人回答蝙蝠侠。 其次,你的例子似乎没有道理。如果你将一个单例的实例传递给一个对象,然后其他对象决定它需要单例,那么该模式允许 - 它应该得到一个引用回到当前在内存中的对象。我所做的一点是,这种模式并不妨碍你处理这个物体。在这种情况下,使用你的例子,如果对象被丢弃,那么单个实例应该不再存在,所以当一个不同的对象请求单例时,它应该得到一个新的实例。 – 2012-02-09 23:34:46

+2

如果对象被丢弃,实例*仍然存在 - 任何在它被丢弃之前获得它的实例仍然会存在。所以它显然不能被GCed,但它现在无法使用。在多线程环境下考虑该声明的后果。像`Singleton.getInstance()。doStuff()`这样的表达式不再是线程安全的,*即使getInstance和doStuff都是*。一个线程可以在两个调用之间出现,并处理我们刚获得的实例。这甚至假设你是神话般的每个人总是调用``getInstance`场景。 – cHao 2012-02-10 04:40:49

3

只要您的终结器不调用任何其他管理对象上的方法(如Dispose),您应该没问题。请记住,最终订单不是确定性的。也就是说,如果你的单件对象美孚持有参照对象吧,需要处理,你不能可靠地写:

~Foo() 
{ 
    Bar.Dispose(); 
} 

垃圾收集器会收集酒吧了。

在进入一堆OO goo(即开始战争)的风险中,使用单例的一种替代方法是使用静态类。

+0

+1对于静态类 - 单身并不像其普遍性所暗示的那样必要。 – 2009-01-21 01:11:52

2

虽然它可能会让您阅读代码复审抱怨和FxCop警告,但是在没有IDisposable的情况下实现终结器没有任何内在错误。但是,在单例中这样做并不是捕获进程或AppDomain拆卸的可靠方法。

存在提供主观设计建议的风险:如果对象是真正的无状态的,则将其设为静态类。如果它是有状态的,请问为什么它是一个Singleton:你正在创建一个可变的全局变量。如果您试图捕捉应用程序关闭,请在您的主循环退出时处理它。

0
辛格尔顿

适用于任何特定的情况之外,

我认为没有什么不妥S的处置ingleton。结合懒惰的实例化,它只是意味着如果您暂时不需要它,则释放资源,然后根据需要重新获取资源。

0

如果你想创建一个带有终结器的单例,你可能应该有一个对它的静态引用是WeakReference。这将需要一些额外的工作来确保访问器中的线程安全,但是当没有人使用它时,它将允许单例进行垃圾收集(如果某人随后调用GetInstance()方法,他们将得到一个新实例)。如果使用静态强引用,那么即使没有其他引用,单例实例也会保持活动状态。