都有点记不起认识单例模式(也有叫单件模式的)是在什么时候了,有时候东西认多了不常用的话也经常抛之脑后甚至逐渐从大脑里被移除。不闲扯了,直接入正题吧。
什么是单例模式?
保证在整个应用程序的生命周期中,在任何时刻,被指定的类只有一个实例,并为客户程序提供一个获取该实例的全局访问点.
单例模式的作用?
被指定的类在整个应用程序只会被创建一次.
接着我们用代码来加深对单例模式的理解。在这之前我们都知道如果一个类在没有使用单例模式的情况下是可以被实例化多次对象的,比如下面代码:
这里我先创建一个类:
1 public class Person 2 { 3 public string Name { get; set; } 4 public string Email { get; set; } 5 public int Age { get; set; } 6 }
在应用程序中,我可以根据自己的需要无限的去实例化这个对象
static void Main(string[] args) { for (int i = 0; i < 100; i++) { Person p = new Person(); } }
这个时候我们开始把单例模式的思想带进来,首先我们思考第一个问题怎么实现这个类只会能被实例化一次对象。之所以我们现在可以无限去new这个对象是因为这个类当前有一个默认的public的构造函数,谁都能去拿
它来进行新的实例化。因此,这样也给我们带来一个解决问题的思路。看下面的代码,我在类中把默认的public构造函数给覆盖掉,自己写一个构造函数然后把尝试把访问修饰符改为private
public class Person { private Person() { } public string Name { get; set; } public string Email { get; set; } public int Age { get; set; } }
这个时候在外部去创建这个对象时就会出问题了。"....不可访问,因为...受保户级别限制"
这个时候虽然无法在外界创建这个对象,但是我们依然可以通过类的内部去创建对象。(在类中可以定义一个方法,这个方法就是用来创建这个对象)这样我们从外界就可以直接通过这个类的方法来创建对象了
这里需要注意的是我们在类中定义创建对象的方法必须为静态方法,这样外界只能通过这个类来实例对象,而不需要再new一个对象再调用这个内部方法。代码如下:
class Program { static void Main(string[] args) { Person p1 = Person.GetInstance(); } } public class Person { private Person() { } public static Person GetInstance() { return new Person(); } public string Name { get; set; } public string Email { get; set; } public int Age { get; set; } }
这个时候依旧存在一个问题,我们依旧可以创建N此对象。这样就跟单例模式的思想不沾边了。接下来我们可以继续在这个类去加一个对象的判断,请看下面的代码
1 public class Person 2 { 3 private Person() 4 { 5 6 } 7 private static Person _instance =null; 8 9 public static Person GetInstance() 10 { 11 if (_instance == null) 12 { 13 return new Person(); 14 } 15 return _instance; 16 17 } 18 public string Name { get; set; } 19 public string Email { get; set; } 20 public int Age { get; set; } 21 }
这个时候我们在这个类中加入静态成员,而静态成员在应用程序中只共享一个类,当第一次调用GetInstance()方法的时它为null,然后满足条件后返回一个新的对象给用户。当第二次进来时,就不会null了,直接
把第一次的的对象返回给用户。解下来我们来测试下这个单例模式的雏形吧,测试结果:
这样就完成了一个基本的单例模式了,但这样还是完全不够完美的。毕竟这种实现无法满足多线程的场景。先撇开这个问题,先来总结下单例模式的实现思路:
把构造函数变成私有的外界就无法随便调用,然后在类中自己创建一个方法,在这个方法中new出一个对象,因为这个方法是要new对象的,所以说这个方法不能为实例方法,只能是静态方法。
静态方法每次被调用它就会new一次对象,这样就不是单例模式,所以还需要在这个类中加入一个静态的字段,这个字段就是这个类本身的类型。然后在这个类中判断这个静态字段为null就实例
化一个对象,然后返回。下次再调用的时候就不会为null了
好了,到这边已经解决了我们最开始的问题了。时间过的好快,(快十二点半了!!抓紧!人真是有点贱,玩游戏看电视不在乎多晚,想正儿八经写个文章就关注时间了)接下来看如何去处理多线程的单例模式。
1 public class Singleton 2 { 3 4 public Singleton() 5 { 6 Console.WriteLine("."); 7 } 8 private static Singleton _instancce; 9 private static readonly object syn = new object(); 10 public static Singleton GetInstance() 11 { 12 13 if (_instancce == null) //当对象为null时就锁定 14 { 15 lock (syn) //锁定 16 { 17 if (_instancce == null) //防止多个线程同时访问 18 { 19 _instancce = new Singleton(); 20 return _instancce; 21 } 22 23 } 24 } 25 return _instancce; 26 27 } 28 }
这是通过加锁的方式来处理多线程访问问题,代码不难。这边简单解释下为什么要有两个对象的判断,先从第一个对象来说,因为一旦一开始加锁,代码就会被锁定同时只会有一段代码在执行,这样会影响代码执行性能。所以在锁定之前先去判断对象是否为 null ;第二个判断为 null 的原因是第一个判断不在这个线程中,表示只要满足条件都可以进入,之后的原因参考以上代码注释即可。
临睡之前突然想到还有个办法也能实现单例模式,可以利用 c# 静态字段,在第一次使用类的时候只初始化一次的特性。代码如下:
1 public class Singleton2 2 { 3 4 private Singleton2() 5 { 6 Console.WriteLine("."); 7 } 8 9 public static readonly Singleton2 _instance = new Singleton2(); 10 public static Singleton2 GetInstance() 11 { 12 return _instance; 13 } 14 }
好了,就写到这儿了~good 9