【问题标题】:singleton pattern in C# QuestionC#中的单例模式问题
【发布时间】:2012-06-06 14:51:14
【问题描述】:

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

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

   private Singleton(){}

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

因为 Singleton 实例是 由私有静态成员引用 变量,实例化不 直到班级第一 由对实例的调用引用 财产。因此该解决方案 实现了惰性的一种形式 实例化属性,如 单例的设计模式形式。

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

private static readonly Singleton instance 

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

2) 我有时需要强制班级创建新内存以清除其内容。使用set 这样做安全吗?

set
{
instance = null;
}

【问题讨论】:

  • 惰性实例化的形式说明一切
  • set { } 来自哪里(非常错误)?
  • 你的第二个问题很有道理。

标签: c# singleton


【解决方案1】:

在您提供的示例代码中,单例实例将在第一次访问该类时创建。这意味着您的示例在第一次调用 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】:

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

    这已在 .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

    【讨论】:

      【解决方案3】:

      C# 规范说:

      10.5.5.1 静态字段初始化

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

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

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

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


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

      【讨论】:

        【解决方案4】:

        它在您发布的报价中说:

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

        所以...每当您致电 .Instance 时,或之前的某个时间。

        【讨论】:

        • 问题是该报价不受规范的保证。这导致“或之前的某个时间”部分。
        【解决方案5】:

        静态成员在第一次访问静态成员之前以及在调用静态构造函数之前初始化。

        Read more on MSDN

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-01
          • 2014-06-05
          • 1970-01-01
          • 2011-03-15
          相关资源
          最近更新 更多