处理单例实例(C#)

如果单例实现IDisposable,那么处理和重新创建实例的正确方法是什么? 一种方法是保持_disposed标志并在Instance属性中检查它,但我不确定这是否是正确的方法。 一个简单的例子:

public sealed class Singleton: IDisposable { private static Singleton _instance; private object _lock; private UnmanagedResource _unmanaged; private bool _disposed; private Singleton() { _unmanaged = new UnmanagedResource(); _disposed = false; _lock = new object(); } public UnmanagedResource Unmanaged { get { return _unmanaged; } } public static Singleton Instance { get { if (_instance == null || _disposed) { lock (_lock) { if (_instance == null || _disposed) { _instance = new Singleton(); } } } return _instance; } } public void Dispose() { _disposed = true; try { _unmanaged.Dispose(); } finally { GC.SuppressFinalize(this); } } } 

所以代码喜欢这个是可能的(但是,我同意,它有点击败了拥有Singleton的目的):

 Singleton.Instance.Dispose(); Singleton.Instance.Unmanaged.UseResource(); // Unmanaged shouldn't be null. 

注意:没有必要过分强调Singleton和IDisposable之间的不兼容性,我理解。 当ApppDomain卸载时,我需要Dispose方法来释放非托管资源。 如果您将此类称为Singleton有问题,我可以将其重命名为LoadBalancer。 问题仍将保持不变。 这个LoadBalancer需要是一次性的,因为它的实例不属于任何人,但应妥善处置。

如果你需要替换单例的实例,你应该考虑你的设计。

如果你真的认为你应该这样做,我建议使用2个对象……

一个单独的对象充当代理,并且是一个真正的单例(生命周期结束==进程结束)

这个对象需要公共成员来处理你的单身人士所能拥有的一切,以及一个拥有真正实现对象的私有成员。 所有其他成员重定向到该实现的成员。

第二个对象是可以更换的一次性物体。 确保只有你的单例对象才能持有对它的引用……这样,如果任何一个消费者对象在单例上持有一个引用来阻止你替换对象并不重要……那个引用只会指向代理人

Singleton和Disposable对象在很大程度上是不兼容的概念

  • 单身人士是一种类型的单一实例,可在申请的整个生命周期内使用
  • 一次性物品是指在不再使用后需要及时处理资源的物品。

单身人士通常在进程/ AppDomain的生命周期中说活着。 如果你发现自己想要Dispose它们,那么你可能需要稍微重构一下你的解决方案。

如果问题是在AppDomain卸载期间释放资源,那么AppDomain负责释放资源,并使用负责管理AppDomain生命周期的相同对象。 然后将此资源嵌入到Singleton而不实现IDisposable 。 这将正确地分离出场景的顾虑。

我不知道这是怎么回事。 考虑这个假设的用例:

 using (Singleton instance = Singleton.Instance) { // Do stuff with the instance. } 

您如何防止多个线程同时执行此代码? 一个线程可以调用Dispose而另一个线程仍在尝试使用相同的实例。 尝试将具有循环语义的API强制转换为单例概念就像尝试将方形挂钩装入圆形支架中一样。

顺便说一句,值得一提的是切线问题。 Instance属性不是线程安全的。 至少你必须将_instance标记为volatile 。 只有当您使用模式的规范实现时才会这样。 您永远无法使模式安全,因为您还使用_disposed标志作为检查中的额外条件。 就个人而言,我只会废弃双重检查的锁定模式。

也许我问一个愚蠢的问题,但为什么你只想处理一个Singleton来创建一个新的? 是不是有一个实例的目的….

这可能是我不知道的设计问题。 在这种情况下,“正确的方法”可能是重新评估您需要代码执行的操作以及您可以使用的模式。