【问题标题】:static class "Initialize" pattern in C#?C#中的静态类“初始化”模式?
【发布时间】:2012-03-16 22:12:46
【问题描述】:

使用这种模式有什么好处或理由?..

public sealed class myStaticClass
{
    private static bool _initialized;
    private static object _lockObject;

    private static string _someStaticField;
    private static int _anotherStaticField;
    private static string _nthStaticField;

    static myStaticClass()
    {
        _initialized = false;  
        _lockObject = new object();
    }

    public myStaticClass()
    {
    }

    public static void Initialize()
    {
        if(!_initialized)
        {
            lock(_lockObject)
            {
                if(!_initialized)
                {
                    //do initializing
                    _someStaticField = someApplicationSetting;
                    _anotherStaticField = anotherApplicationSetting;
                    _nthStaticField = nthApplicationSetting;

                    _initialized = true;
                }
            }
        }
    }

    public static string NthStaticField 
    { 
        get {

            Initialize();
            return _nthOtherField;
        }
    }
}

如果保证在访问任何类成员之前调用静态构造函数,并且只调用一次,那么为什么不将所有初始化逻辑放在静态构造函数中呢?

编辑:我已经更新了模式,以更好地反映我发现它的 .net 框架中的类。我已将类上的static 修饰符更改为sealed,并且还添加了一个空的public 构造函数。

ps。如果您想知道,我看到这种模式的类是 FormsAuthentication 类。

【问题讨论】:

  • 查找 C# 的“双重检查锁定”和/或“延迟初始化”。有一篇很好的文章链接到几篇文章,讨论了获得这种惰性 [one-time-when-needed] 初始化的不同方法。 (而且发布的代码实际上不是在许多情况下推荐的方式之一;-)
  • 啊,这里是来自 C# In Depth 的著名文章:csharpindepth.com/Articles/General/Singleton.aspx 列出了 5 种方法,并附有解释。
  • @pst 是对的,但应该更有力。 这段代码被严重破坏和错误。 你不能像这样安全地进行双重检查锁定!双重检查锁定的基本规则是测试的东西必须是唯一你改变的东西。你不能使用一个标志来控制双重检查锁定,然后改变一些完全其他的变量;这决不能保证是安全的。如果您不完全了解运行时的内存模型,则不应尝试编写低锁代码。
  • 在 pst 发布的第一个链接中阅读我的答案;这是著名的“危险地破坏错误实现的双重检查锁定模式”的又一个例子。

标签: c# static initialization


【解决方案1】:

这样做的唯一原因是,如果初始化很昂贵(就 CPU/RAM 而言),或者需要的东西只能在运行时 IMO 的后期才可用...此实现将初始化延迟到可能的最新时刻(就在第一次访问之前)。这有点像Lazy<T> 提供的...

【讨论】:

    【解决方案2】:

    可能,ApplicationSettings 直到稍后在运行时才可用。

    【讨论】:

      【解决方案3】:

      好的,我现在已经阅读并考虑了给出的链接,并得出结论,这个特定的例子没有被破坏。

      我会解释为什么...

      首先它与 csharpindepth 文章csharpindepth.com/Articles/General/Singleton.aspx 中的模式不同。本文中的模式适用于单例。我提出的模式不是单例,它有一个公共构造函数,但是它的所有字段都是静态的。所以最大的区别是 csharpindepth 模式返回一个对单例对象的引用,即使它实际上可能没有被完全实例化。关键是可以在构造函数完成之前将实例分配给引用。

      public sealed class Singleton
      {
       private static Singleton instance = null;
       private static readonly object padlock = new object();
      
      Singleton()
       {
       }
      
      public static Singleton Instance
       {
           get
           {
               if (instance == null)
               {
                   lock (padlock)
                   {
                       if (instance == null)
                       {
                           instance = new Singleton();
                       }
                   }
               }
               return instance;
           }
       }
      } 
      

      这是 csharpindepth 模式,它的工作原理是这样的..

      如果有3个线程A、B、C。线程A调用Instance方法,instance为null,获取锁。现在,在线程 A 创建 Sigleton 实例之前,线程 B 出现,实例仍然为空。但是 A 有锁,所以 B 等待。当 B 等待时,线程 A 执行代码instance = new Instance()。此时 instance 被设置为 Singleton 的一个实例,但是它可能还没有真正被构造。 这是线程安全中断的地方。 随之而来的是线程 C,它看到实例不为空,因此 Instance 方法返回对实例的引用。 但是线程 A 还没有完成实例的构造,因此线程 C 和与线程 C 同时进入的任何其他线程都获得了对一个哑单例实例的引用。最后 A 完成,B 获得了锁,但现在工作已经完成,所以它退出了。

      我提出的模式明显不同。 用于双重检查的字段不是在 Initialize 方法中创建的实例。它是一个简单的private static bool,用作开关。由于它不是对尚未构造的 Singleton 对象的引用,并且由于它从未在此类之外访问过,因此没有问题。

      如果 private static bool _initialized 开关在类的字段被初始化之前在顶部设置为 true,则会出现问题,但事实并非如此。这是代码放弃锁定之前发生的最后一件事。因此,线程 C 不可能读取 _initialized 并将类误认为已初始化。所以无论如何它都不是。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-13
        • 2021-10-21
        • 2011-08-26
        • 2012-05-04
        • 2015-05-18
        • 1970-01-01
        • 2023-03-31
        • 1970-01-01
        相关资源
        最近更新 更多