设计模式之单例模式

所谓单例(Singleton)就是在应用程序运行期间,某个类型对外公布的实例始终是同一个,同一个的意思并不是说相等的,而是相同的,我们可以利用object的一个静态方法object.ReferenceEquals(而非object.Equals)来测试单例。
1、首先,该Singleton的构造函数必须是私有的,以保证客户程序不会通过new()操作产生一个实例,达到实现单例的目的。
2、其次,尽可能的把单例的类用sealed关键字修饰,防止类被继承。
2、因为静态变量的生命周期跟整个应用程序的生命周期是一样的,所以可以定义一个私有的静态全局变量instance来保存该类的唯一实例;
3、必须提供一个全局函数或者静态属性访问获得该实例,并且在该函数或者属性内部提供控制实例数量的功能,即通过if语句判断instance是否已被实例化,如果没有则用new()创建一个实例;否则,直接向客户返回一个实例。

测试代码

class Program
    {
        static void Main(string[] args)
        {
            Singleton instance1 = Singleton.Instance;
            Singleton instance2 = Singleton.Instance;
            bool result = object.ReferenceEquals(instance1, instance2);
            Console.WriteLine(result);
            Console.ReadKey();
        }
    }
 

版本一

public sealed class Singleton
    {
        private static Singleton _instance;
        private Singleton() { }
        public static Singleton Instance
        {
            get
            {
                if(_instance==null)
                {
                    _instance = new Singleton();
                }
                return _instance;
            }
        }
    }
 

说明:这个版本中,没有考虑线程并发获取实例问题,即可能出现两个线程同时获取instance实例,且此时其为null时,就会出现两个线程分别创建了instance,违反了单例规则。

版本二

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

说明:这个和版本一唯一的区别就是保证了线程安全,使用了双重锁方式较好地解决了多线程下的单例模式实现。先看内层的if语句块,使用这个语句块时,先进行加锁操作,保证只有一个线程可以访问该语句块,进而保证只创建了一个实例。再看外层的if语句块,这使得每个线程欲获取实例时不必每次都得加锁,因为只有实例为空时(即需要创建一个实例),才需加锁创建,若果已存在一个实例,就直接返回该实例,节省了性能开销.(本人认为不需要加入volatile关键字修饰)。

版本三

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

说明:这个版本保证了在第一次使用时Singleton已经初始化了一个实例对象,比较简单方便,比较常用,不过有一点值得我们关注,那就是和版本四的比较,显示实现静态构造函数。

版本四

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

说明:这个版本和版本三基本一样,多了一个静态的构造函数,目的是为了消除IL代码中的beforefieldinit标记,在C#中,如果显示实现了静态构造函数则,则静态构造函数的执行是严格按照需要来执行的,即在第一次调用静态成员之前执行,如果没有显示实现静态构造函数,则静态构造函数的执行时机是随意的(只要保证在静态成员之前的任何时候都可以)。

详细的请参考Artech的关于Type Initializer和 BeforeFieldInit的问题,看看大家能否给出正确的解释 和 涛哥的.Net类型构造器 

版本五

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

       // private static readonly Lazy<Singleton> _instance = new Lazy<Singleton>(()=>new Singleton(),System.Threading.LazyThreadSafetyMode.ExecutionAndPublication);
        private Singleton() { }
        static Singleton() { }
        public Singleton Instance
        {
            get
            {
                return _instance.Value;
            }
        }
    }

说明:这个版本实用了Lazy,个人感觉和版本四效果一样,都可以启到延迟加载的效果,上面注释掉的代码是设置线程安全的(由于Lazy本就是线程安全的,所以不设置也是一样的)。

版本六(网上搜的)

 public  abstract class Singleton<T> where T:class
    {
        private static readonly Lazy<T> _instance
          = new Lazy<T>(() =>
          {
              var ctors = typeof(T).GetConstructors(
                  BindingFlags.Instance
                  | BindingFlags.NonPublic
                  | BindingFlags.Public);
              if (ctors.Count() != 1)
                  throw new InvalidOperationException(String.Format("Type {0} must have exactly one constructor.", typeof(T)));
              var ctor = ctors.SingleOrDefault(c => c.GetParameters().Count() == 0 && c.IsPrivate);
              if (ctor == null)
                  throw new InvalidOperationException(String.Format("The constructor for {0} must be private and take no parameters.", typeof(T)));
              return (T)ctor.Invoke(null);
          });
        public static T Instance
        {
            get { return _instance.Value; }
        }
    }

说明:这是一个单例的基类,用abstract修饰,保证所有需要实用单例的类要继承它,实用反射创建类型对象,也不用担心效率问题,因为只执行一次,好处是写一个基类之后,其它需要实现单例的类就不要写太多代码(其实也不多哦)去实现单例了,但是注意私有构造函数还是必须的。

结束语

对于单例,有很多的实现方式,上面是几种简单的实现模式,个人认为没必须要搞的太复杂,只需要简单实用即可,希望对你有所帮助。

 

posted @ 2014-12-20 16:11  Aseven  阅读(1283)  评论(0编辑  收藏  举报