一段时间后使Singleton实例失效

时间:2009-09-17 14:54:36

标签: c# design-patterns singleton

暂时搁置一下关于Singleton模式的相对优点和缺点的论点,并考虑到Singleton通常被认为是一个在应用程序的生命周期中持续存在的实例,那么最好的方法是什么?有一个生命有限的单身人士?

是否有类似以下内容的错误:

public class CategoryHandler
{    

    private static DateTime m_expires;

    public bool HasExpired
    {
        get return DateTime.Now > m_expires;
    }

    private CategoryHandler()
    {
        m_expires = DateTime.Now.AddMinutes(60);
    }

    public static CategoryHandler Instance()
    {
        if(HasExpired)
        {
            //Dispose and reconstruct
        }
        else
        {
            //Use existing instance
        }
    }

}

或者有更好的方法来解决这个问题吗?

5 个答案:

答案 0 :(得分:4)

您需要某种锁定来确保线程安全:

public sealed class CategoryHandler
{
    private static CategoryHandler _instance = null;
    private static DateTime _expiry = DateTime.MinValue;
    private static readonly object _lock = new object();

    private CategoryHandler() { }

    public static bool HasExpired
    {
        get
        {
            lock (_lock) { return (_expiry < DateTime.Now); }
        }
    }

    public static CategoryHandler Instance
    {
        get
        {
            lock (_lock)
            {
                if (HasExpired)
                {
                    // dispose and reconstruct _instance
                    _expiry = DateTime.Now.AddMinutes(60);
                }
                return _instance;
            }
        }
    }
}

答案 1 :(得分:2)

我能看到的唯一主要问题是,另一个类已经有了对旧实例的引用,这不会使它失效。所以只要所有类都这样做:

CategoryHandler.Instance.Method();

而不是

CategoryHandler singleton = CategoryHandler.Instance;
...
singleton.SomeMethod();

你应该没事,只要你对单身人士下次被引用而不是在六十分钟之后过期感到高兴。

如果你需要它在一段时间之后到期,那么你将需要使用一个计时器并在回调方法中替换实例(注意计时器将在另一个线程中回调,所以一定要实现一个线程安全的单身人士模式)

答案 2 :(得分:2)

为了避免类引用旧实例的问题,您可以考虑为您的类编写缓存包装器。 Fist提取接口ICategoryHandler,假设它看起来像这样:

interface ICategoryHandler 
{
  int A();
}

然后实现包装器(省略锁定):

class CategoryHandlerWrapper : ICategoryHandler
{
  ICategoryHandler instance;
  private DateTime expiry = DateTime.MinValue;

  public int A()
  {
    return Instance().A();
  }

  public bool HasExpired
    {
        get return DateTime.Now > expiry;
    }

  private CategoryHandler Instance()
    {
        if(HasExpired)
        {
            //Dispose and reconstruct
        }
        else
        {
            //Use existing instance
        }
    }
}

通过这种方式,您可以使用普通依赖注入,并仍然享受您所追求的功能。此外,旧引用的问题也封装在一个地方。

答案 3 :(得分:1)

您是否考虑过使用IoC/DI Container framework并让它管理“单身人士”的生命周期?

微软的Unity是我最熟悉的,他们没有提供开箱即用的LifetimeManager,它完全符合您的要求。但是,你可以创建自己的后代;见Writing Custom Lifetime Managers

或者您可以查看其他框架,看看框中是否有您想要的内容。

编辑:请注意,这仍然不能让你摆脱马丁提到的“捕获”问题。

答案 4 :(得分:0)

你想用即将到期的单身人士到底想要达到什么目的?如果你真的想这样做,我认为你的设计有问题。正如马丁所说,如果某些代码“捕获”了你的单例实例,你就会遇到麻烦。

那么,您对此有何用处?或者问题出于好奇心?