在C#中事项单态模式的详细介绍

在C#中事项单态模式的详细介绍

单态模式是软件工程中众所周知的设计模式之一。本质上讲,单态模式就是一个类只允许有一个它自己的实例被创建,通常情况下它提供了对这个实例的单一访问。大多数情况下,单态一般在创建实例时不允许指定任何参数,当另外提供对所有的请求提供一个不同参数的实例方法的时候可能会有问题!(如果对于所有带有相同参数的请求,同一个实例都允许被访问,工厂模式可能更合适。)这篇文章只处理没有参数要求的情形。单态模式具有代表性的是迟创建,例如只有当需要的时候实例才被创建。

在C#中单态模式有多种不同的实现方式。在这里我将以次序颠倒的介绍他们,最开始是最常见的,但不是线程安全的,然后逐步实现一个完全的迟加载,线程安全,简单且高效的版本。注意在代码里,我省略了private修饰符,因为对于类成员默认就是private.在其他许多语言里例如Java ,都有不同的默认方式,并且private应该被使用。

所有的这些实现都有四种共同的特性,但是:
1、单一的构造器,它是私有的,并且没有参数。这保护了其他类实现它(这将是对模式规则的违犯)。注意它也保护了子类-如果一个单态能够被继承,它能不继承2次,并且如果这些子类能创建实例,模式规则就被打破了。日过你需要一个基类的单一的实例,工厂模式能够使用,但是直到运行时才知道严格的方法。
2、类被封闭。这是多余的,严格的讲由于前面几点,但是可以帮助JIT识别。
3、任何情况下,一个保存单一引用的静态变量创建实例
4、用来获得单一创建实例应用的公共的静态变量,必要的时候再创建

注意所有这个实现都使用了一个公共的静态属性Instance作为访问实例的方法。在所有的Case中,属性能很容易的转换成方法,而对线程安全和执行没有任何的影响

第一个:非线程安全的

// Bad code! Do not use!
public sealed class Singleton
{
  static Singleton instance=null;

  Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      if (instance==null)
      {
        instance = new Singleton();
      }
      return instance;
    }
  }
}
正如之前说的,上面的代码并不是线程安全的。2个不同的线程能够都进行测试if(instance==null)就会发现结果为true,接着2个都创建实例,这就违反了单态模式的规则。注意事实上实例可能在表达式执行之前就已经创建了,但是内存模式并不保证实例的新值已经被另外的进程所知,除非已经分配了适当的内存组。
第二个:简单的线程安全
public sealed class Singleton
{
  static Singleton instance=null;
  static readonly object padlock = new object();

  Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      lock (padlock)
      {
        if (instance==null)
        {
          instance = new Singleton();
        }
        return instance;
      }
    }
  }
}
这个实现是线程安全的。线程对共享资源进行了锁定,接着检查是否实例在创建实例之前已经实现。这样就考虑到了内存组以及确保只有一个线程创建实例。不幸的是,在实例被请求的时候lock总是每次都执行。
注意不使用象在这个例子中的对Singleton对象的锁定,我锁定了这个类的私有的静态变量的值。锁定其他类访问访问和锁定的对象是冒风险的方式,可能会导致死锁。这是我通常的实现方式,无论在哪里只要有可能就只锁定制定的想要锁定的对象,或者证明他们是为了特殊的目的被锁定。通常情况下,这种对象在使用他们的雷中应该是私有的。这样有助于更容易编写线程安全的应用程序。
第三个:尝试使用二次确认锁定实现线程安全

// Bad code! Do not use!
public sealed class Singleton
{
  static Singleton instance=null;
  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;
    }
  }
}

这个实现试图在线程安全的基础上去掉每次的锁定时间。不幸的是,在这种方式中有如下4个劣势:
1、在Java中不能使用。这看起来可能是件器官的事情,但是它值得引起我们的注意如果你究竟需要单态模式是在Java还是C#以及Java。Java的内存模式不能确保构造器在对新引用完成之前分配实例。Java内存模式将通过重新工作(1.5 版本),而不是预先二次确认锁定。
2、抛开内存组,在.net中也是破方法。
3、容易出错
4、它并没有晚实现完成的好

第四个非迟实现,但是脱离了lock实现线程安全

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

  // Explicit static constructor to tell C# compiler
  // not to mark type as beforefieldinit
  static Singleton()
  {
  }

  Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      return instance;
    }
  }
}
第5个 完全的迟实例化

public sealed class Singleton
{
  Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      return Nested.instance;
    }
  }
  
  class Nested
  {
    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Nested()
    {
    }

    internal static readonly Singleton instance = new Singleton();
  }
}