C#问题中的singleton模式

本文关键字:singleton 模式 问题 | 更新日期: 2023-09-27 17:58:52

我正在研究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;
}

C#问题中的singleton模式

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

您可以在Jon Skeet的文章《在C#中实现Singleton模式》中找到更多见解,请参阅方法4。

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

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();
    static Singleton(){}
    private Singleton(){}
    public static Singleton Instance
    {
        get { return instance; }
    }
}

足够了。

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

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

引用上述文章:

只有当类型没有用名为beforefieldinit的特殊标志标记时,.NET才能保证类型初始化程序的惰性。不幸的是,C#编译器[…]将所有没有静态构造函数[…]的类型标记为beforefieldinit

编辑2
如果您希望清理singleton对象,则应该将此功能包含到类Singleton本身中。

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

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

这在.NET 4中通过新的Lazy<T>实现得到了修复。

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

C#规范说:

10.5.5.1静态场初始化

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

即,他们只保证在读取instance字段之前发生。但它可能发生得更早。

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

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


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

实例化直到类首先由调用实例属性

所以。。。无论何时调用.Instance,或之前的某个时刻。

在首次访问静态成员之前以及在调用静态构造函数(如果有)之前初始化静态成员。

阅读MSDN 上的更多信息