处理单例实例(C#)

时间:2011-06-03 15:23:49

标签: c# .net-3.5 singleton

如果单例实现了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需要是一次性的,因为它的实例不属于任何人,但应妥善处置。

4 个答案:

答案 0 :(得分:6)

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

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

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

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

答案 1 :(得分:6)

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

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

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

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

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

答案 2 :(得分:2)

我没看到这是怎么回事。考虑这个假设的用例:

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

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

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

答案 3 :(得分:0)

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

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