先来从生活中找到Singleton模式的需求案例来理解什么是Singleton模式(我们从网上可以找到如下案例):
1、美国总统的职位是唯一的,美国宪法规定了总统的选举,任期以及继任的顺序。这样,在任何时刻只能由一个现任的总统。无论现任总统的身份为何,其头衔"美利坚合众国总统"是访问这个职位的人的一个全局的访问点。
2、中国古代皇帝玉玺,也是只能有一个,不管在谁手里,是谁使用,都只能保证只有这么一个玉玺,有了它,才能拥有它所代表的一切权力,行使它所赋予的相关职责。
因此, Singleton模式要求一个类有且仅有一个实例,并且提供了一个全局的访问点。这就提出了一个问题:如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?客户程序在调用某一个类时,它是不会考虑这具类是否只能有一个实例等问题的,所以,这应该是类设计者的责任,而不是类使用者考虑的问题。
现在我们就一起来实现这种模式,Singleton模式的实现要分成单线程情况下的实现与在多线程情况下的实现两种情况.
一、在单线程情况下的实现
#region 单线程情况下的Singleton
#region 无参数的Singleton
#region 定义方式一
public class Singleton
{
private static Singleton instance;
private Singleton(){} //让类的使用者调用不到构造器
//创建一个静态属性,通过调用此属性来得到此类最初创建的实例
public static Singleton Instance
{
get
{
if(instance==null) //刚开始时instance肯定为NULL
{
instance=new Singleton(); //在此处可以调用到Singleton构造函数
Console.WriteLine("创建了这个实例!");
}
return instance;
}
}
}
#endregion
#region 定义方式二
public class Singleton2
{
private static Singleton2 instance=new Singleton2(); //在设计私有变量时就创建了一个对象实例
private Singleton2(){} //让类的使用者调用不到构造器
//创建一个静态属性,通过调用此属性来得到此类最初创建的实例
public static Singleton2 Instance
{
get
{
return instance;
}
}
}
#endregion
#endregion
#region 带参数的Singleton
#region 定义方式
public class Singleton5
{
int _x;
int _y;
private static Singleton5 instance;
//把构造函数设置为Private,从而让类的使用者调用不到构造器
private Singleton5(int x, int y)
{
this._x=x;
this._y=y;
}
//创建一个静态属性,通过调用此属性来得到此类最初创建的实例
public static Singleton5 GetInstance(int xi, int yi) //要通过方法,而非属性来传参了
{
if(instance==null) //刚开始时instance肯定为NULL
{
instance=new Singleton5(xi,yi); //在此处可以调用到Singleton构造函数
}
return instance;
}
}
#endregion
#endregion
#endregion
#region 无参数的Singleton
#region 定义方式一
public class Singleton
{
private static Singleton instance;
private Singleton(){} //让类的使用者调用不到构造器
//创建一个静态属性,通过调用此属性来得到此类最初创建的实例
public static Singleton Instance
{
get
{
if(instance==null) //刚开始时instance肯定为NULL
{
instance=new Singleton(); //在此处可以调用到Singleton构造函数
Console.WriteLine("创建了这个实例!");
}
return instance;
}
}
}
#endregion
#region 定义方式二
public class Singleton2
{
private static Singleton2 instance=new Singleton2(); //在设计私有变量时就创建了一个对象实例
private Singleton2(){} //让类的使用者调用不到构造器
//创建一个静态属性,通过调用此属性来得到此类最初创建的实例
public static Singleton2 Instance
{
get
{
return instance;
}
}
}
#endregion
#endregion
#region 带参数的Singleton
#region 定义方式
public class Singleton5
{
int _x;
int _y;
private static Singleton5 instance;
//把构造函数设置为Private,从而让类的使用者调用不到构造器
private Singleton5(int x, int y)
{
this._x=x;
this._y=y;
}
//创建一个静态属性,通过调用此属性来得到此类最初创建的实例
public static Singleton5 GetInstance(int xi, int yi) //要通过方法,而非属性来传参了
{
if(instance==null) //刚开始时instance肯定为NULL
{
instance=new Singleton5(xi,yi); //在此处可以调用到Singleton构造函数
}
return instance;
}
}
#endregion
#endregion
#endregion
二、在多线程情况下的实现
#region 多线程情况下的Singleton
#region 多线程定义方式一
public class Singletone3
{
private static volatile Singletone3 instance=null; //使用volatile的目的是避免编译器在编译阶段对下面get代码段的代码执行顺序进行微调,从而避免潜在的lock功能失效
private static object lockHelper=new object(); //定义了一个辅助器,它本身不参与真正意义上的构建,主要目的是配合lock语句的需要
private Singletone3(){}
public static Singletone3 Instance
{
get
{
if(instance==null)
{
lock(lockHelper) //通过lock来避免多线程的冲突操作
{
if(instance==null) //双检查
{
instance=new Singletone3();
}
}
}
return instance;
}
}
#endregion
#region 多线程定义方式二
//sealed class Singleton4//如果不需要子类去继承它则sealed掉
//其缺点是不支持参数化的Singleton
public class Singleton4
{
public static readonly Singleton4 Instance=new Singleton4(); //通过内联初始化来实现多线程情况下的Singleton的实现
#region 上一句的等价代码
// public static readonly Singleton4 Instance;
// static Singleton4() //放在一个静态构造器中进行实例的初始化,因为静态构造器保证了在多线程情况下,只可能有一个线程对其进行操作,而不可能同时多个操作
// {
// Instance=new Singleton4();
// }
#endregion
private Singleton4(){} //构造器私有化
}
#endregion
#region 多线程定义方式一
public class Singletone3
{
private static volatile Singletone3 instance=null; //使用volatile的目的是避免编译器在编译阶段对下面get代码段的代码执行顺序进行微调,从而避免潜在的lock功能失效
private static object lockHelper=new object(); //定义了一个辅助器,它本身不参与真正意义上的构建,主要目的是配合lock语句的需要
private Singletone3(){}
public static Singletone3 Instance
{
get
{
if(instance==null)
{
lock(lockHelper) //通过lock来避免多线程的冲突操作
{
if(instance==null) //双检查
{
instance=new Singletone3();
}
}
}
return instance;
}
}
#endregion
#region 多线程定义方式二
//sealed class Singleton4//如果不需要子类去继承它则sealed掉
//其缺点是不支持参数化的Singleton
public class Singleton4
{
public static readonly Singleton4 Instance=new Singleton4(); //通过内联初始化来实现多线程情况下的Singleton的实现
#region 上一句的等价代码
// public static readonly Singleton4 Instance;
// static Singleton4() //放在一个静态构造器中进行实例的初始化,因为静态构造器保证了在多线程情况下,只可能有一个线程对其进行操作,而不可能同时多个操作
// {
// Instance=new Singleton4();
// }
#endregion
private Singleton4(){} //构造器私有化
}
#endregion
三、测试结果
class MainClass
{
public static void Main(string[] args)
{
//无参数
Singleton mySingle1= Singleton.Instance;
Singleton mySingle2= Singleton.Instance;
Console.Write(object.ReferenceEquals(mySingle1,mySingle2)==true);//看看这两个类是否是同一个类
//带参数
Singleton5 mySingle51= Singleton5.GetInstance(6,8);
Singleton5 mySingle52= Singleton5.GetInstance(7,8); //第二次已经无效,因为是Singleton
Console.Read();
}
}
{
public static void Main(string[] args)
{
//无参数
Singleton mySingle1= Singleton.Instance;
Singleton mySingle2= Singleton.Instance;
Console.Write(object.ReferenceEquals(mySingle1,mySingle2)==true);//看看这两个类是否是同一个类
//带参数
Singleton5 mySingle51= Singleton5.GetInstance(6,8);
Singleton5 mySingle52= Singleton5.GetInstance(7,8); //第二次已经无效,因为是Singleton
Console.Read();
}
}
总结说明:
Singleton的实现过程实质是通过控制构造对象的过程来实现对构造对象个数的控制
单线程下的单件模式有几点要注意:
1、Singleton模式中的实例构造器可以设置为Protected以允许子类派生
2、Singleton模式一般不要支持ICloneable接口,因为这可能会导致多个对象实例,与Singleton模式的设置初衷违背
3、Singleton模式一般不要支持序列化,因为这也有可能导致多个对象实例(序列化是生成对象实例的另一种方法:先序列化再反序列化从而在内存中产生对象的另一个实例,当然通过构造函数创建对象实例是我们通常认可的方式)
4、Singleton模式只考虑到了对象创建的管理,没有考虑到对象销毁的管理。就支持垃圾回收的平台和对象的开销来讲,我们一般没有必要对其销毁进行特殊的管理
5、不能应对多线程环境:在多线程环境下,使用Singleton模式仍然有可能得到Singleton类的多个实例对象。
2、Singleton模式一般不要支持ICloneable接口,因为这可能会导致多个对象实例,与Singleton模式的设置初衷违背
3、Singleton模式一般不要支持序列化,因为这也有可能导致多个对象实例(序列化是生成对象实例的另一种方法:先序列化再反序列化从而在内存中产生对象的另一个实例,当然通过构造函数创建对象实例是我们通常认可的方式)
4、Singleton模式只考虑到了对象创建的管理,没有考虑到对象销毁的管理。就支持垃圾回收的平台和对象的开销来讲,我们一般没有必要对其销毁进行特殊的管理
5、不能应对多线程环境:在多线程环境下,使用Singleton模式仍然有可能得到Singleton类的多个实例对象。
Singleton模式的扩展
2、将new构造器的调用转移到其它类中,eg:多个类协同工作环境中,某个局部环境只需要拥有某个类的一个实例
3、理解和扩展Singleton模式的核心是“如何控件用户使用new对一个类的实例构造器的任意调用”