这也算是我所学习的第一个设计模式把,因为它最简单。但是仔细学一下就会发现,也不是很简单的,因为需要考虑很多问题。
写一个MDI窗口程序,当中有一个工具箱的窗体,希望工具箱要么不出现,要么出现一个,怎么做到呢?
可以用是否为null来判断,别忘了还有disposed的判断。如果有多处的话要写成函数。
但是,这样的话把是否实例化都写到Form中了,而Form里应该只是通知启动工具箱,至于工具箱窗体啊是否实例化过,这时他自己的责任,而不是别人的责任,别人应该只是使用它就可以了。
所有类都有构造方法,不编码则系统默认生成空的构造方法,若有显示定义的构造方法,默认的构造方法就会失效。
于是我们可以将工具箱类的构造方法写成是private的,对于外部代码,不能用new来实例化它,但是我们可以用GetInstance()方法,这个方法的目的就是返回一个类实例,在此方法中,去做是否有实例化的判断。如果没有实例化过,由调用private的构造方法new出这个实例。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Singleton
{
public partial class FormToolBox : Form
{
//声明一个静态的类变量
private static FormToolBox ftb = null;
//构造方法私有,外部代码不能直接new来实例化它
private FormToolBox()
{
InitializeComponent();
}
private void FormToolBox_Load(object sender, EventArgs e)
{
}
//得到类实例的方法,返回值就是本类对象,注意也是静态的。
public static FormToolBox GetInstance()
{
//从内部的ftb是null或者是Disposed过,则new它,并且设计其MdiParent为Form1,
//此时将实例化的对象存在静态的变量ftb中,以后就可以不用实例而得到他了。
if (ftb == null || ftb.IsDisposed)
{
ftb = new FormToolBox();
ftb.MdiParent = Form1.ActiveForm;
}
return ftb;
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Singleton
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
}
private void 工具箱ToolStripMenuItem_Click(object sender, EventArgs e)
{
FormToolBox.GetInstance().Show();
}
private void toolStripButton1_Click(object sender, EventArgs e)
{
FormToolBox.GetInstance().Show();
}
}
}
单例模式:保证一个类仅有一个实例,并提供一个访问他的全局访问点。
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象,一个最好的办法就是,让类自身负责保存它的唯一实例,这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
单例模式因为Singleton类封装他的唯一实例,这样他可以严格地控制客户怎样访问它以及何时访问它,简单的说就是对唯一实例的受控访问。
在多线程的情况下我们怎么做呢?多个线程同时访问Singleton类,调用GetInstance()方法,会有可能造成创建多个实例的。
可以用lock。lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区,如果其他线程试图进入锁定的代码,则它讲一直等待,直到该对象被释放。
但是每次都要lock,很影响性能。
可以用双重锁定的方法:
class Singleton
{
private static Singleton instance = null;
private static readonly Object syncObject = new Object();
private Singleton()
{
}
public static Singleton GetInstance()
{
if (instance == null)
{
lock (syncObject)
{
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
在外面已经判断了instance实例是否存在,为什么在lock里面还需要做一次instance实例是否存在的判断呢?
对于instance存在的情况,就直接放回,这没有问题。当instance为null并且同时有两个线程调用GetInstance()方法时,他们将都可以通过第一重instance == null的判断,然后由于lock机制,这两个线程则只有一个进入,另一个在外排队等候,必须要其中的一个进入并出来后,另一个才能进入。而此时如果没有了第二重的instance是否为null的判断,则第一个线程创建了实例,而第二个线程还是可以继续再创建新的实例,这就没有达到单例的目的。
C#与公共语言运行库也提供了一种静态初始化的方法,这种方法不需要开发人员显式地编写线程安全代码,即可解决多线程环境下它是不安全的问题。
//阻止发生派生,而派生可能会增加实例
sealed class Singleton
{
//在第一次引用类的任何成员时创建实例,公共语言运行库负责处理变量初始化
private static readonly Singleton instance = new Singleton();
private Singleton()
{
}
public static Singleton GetInstance()
{
return instance;
}
}
这种静态初始化的方式是在自己被加载时就将自己实例化,所以被形象地称之为饿汗式单例类,原先的单例模式处理方式是要在第一次被引用时,才会将自己实例化,所以就被称为懒汉式单例类。