C#问题中的单例模式

时间:2011-06-08 11:34:57

标签: c# singleton

我正在研究C#的单例模式我在msdn网站上找到了这个例子。

public sealed class Singleton
{
   private static readonly Singleton instance = new Singleton();

   private Singleton(){}

   public static Singleton Instance
   {
      get 
      {
         return instance; 
      }
   }
}
  

因为Singleton实例是   由私有静态成员引用   变量,实例化没有   直到班级第一次出现   由对实例的调用引用   属性。因此这个解决方案   实现了一种懒惰的形式   实例化属性,如在   Singleton的设计模式形式。

我不确定何时将内存分配给

private static readonly Singleton instance 

1)在调用Instance属性时甚至在它之前会发生吗?

2)我需要强制类创建一个新的内存来清除其内容。使用set安全吗?

set
{
instance = null;
}

5 个答案:

答案 0 :(得分:7)

在您提供的示例代码中,将在首次访问类时创建单例实例。这意味着在第一次调用Instance时的示例。

您可以在Jon Skeet的文章Implementing the Singleton Pattern in C#中找到更多见解,请参阅方法4.

基本上,为了实现真正的懒惰行为,比如

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();

    static Singleton(){}
    private Singleton(){}

    public static Singleton Instance
    {
        get { return instance; }
    }
}

就够了。

(但无论如何,在上述文章中可以找到完整且更好的概述。)

修改
实际上,正如上面提到的文章所述,由于BeforeFiledInit标记,不保证在第一次访问时创建实例。你需要添加一个空的静态构造函数,这样它保证是懒惰的。否则,将在程序启动和首次访问之间的某个未指定时间创建实例。 (已知.NET运行时2.0具有更加迫切的策略,因此您可能无法获得惰性行为。)

引用上述文章:

  

类型初始值设定项的延迟仅在类型未标记为beforefieldinit的特殊标志时由.NET保证。不幸的是,C#编译器[...]将所有没有静态构造函数的类型标记为beforefieldinit

编辑2
如果要清理单例对象,则应将此功能包含在类Singleton本身中。

删除属性值将有效地使您的类不再是单例!想象一下,有人访问单例并获取单例实例。之后,将属性设置为nullSingleton类的现有对象不会消失,因为它仍然有参考它!因此,下次访问Instance属性时,将创建另一个Singleton类实例。所以你丢失了两件事:你的对象不再是单例(因为你同时存在2个实例),并且内存也没有被清除。

答案 1 :(得分:7)

当加载类本身时,单例实例将被加载到内存中,这时可以调用它的方法开始执行。调用该类的实际行不必实际执行。这是一个非常小的区别,但是当静态构造函数或静态字段初始值设定项可以抛出错误(这里没有)时,可能会产生难以调试的问题。

使用新的Lazy<T>实现在.NET 4中修复了此问题。

http://msmvps.com/blogs/jon_skeet/archive/2010/01/26/type-initialization-changes-in-net-4-0.aspx

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton()
    {
    }
} 

http://csharpindepth.com/Articles/General/Singleton.aspx

答案 2 :(得分:3)

C#规范说:

  

10.5.5.1静态字段初始化

     

类的静态字段变量初始值设定项对应于按照它们出现在类声明中的文本顺序执行的赋值序列。如果类中存在静态构造函数(第10.12节),则在执行该静态构造函数之前立即执行静态字段初始值设定项。否则,静态字段初始值设定项在首次使用该类的静态字段之前的实现相关时间执行。

即。它们只能保证您在读取instance字段之前就会发生这种情况。但它可以更早发生。

如果你想保证它不会比第一次访问属性更早运行,你需要添加一个静态构造函数(可能是空的):

  

封闭类类型的静态构造函数在给定的应用程序域中最多执行一次。静态构造函数的执行由应用程序域中发生的以下第一个事件触发:
  ·创建类类型的实例   ·引用类类型的任何静态成员。


作为一个侧节点:我注意到使用DI时很少需要使用实际的单例。简单地告诉DI它应该创建一个单独的实例就足够了。如果您稍后决定需要多个实例,那么这是非常好的,因为它是一个单例的事实并没有被使用它的所有代码。

答案 3 :(得分:0)

它在你发布的报价中说:

  

直到实例化才会发生   该类首先由a引用   调用Instance属性

所以...每当你打电话给.Instance时,或者之前的某个时刻。

答案 4 :(得分:0)

静态成员在第一次访问静态成员之前初始化,并且在静态构造函数之前(如果有的话)被调用。

Read more on MSDN