单例模式

单例模式:类只能实例化一次,并且外部只有一个入口去访问该实例。
总结一下就是:受控访问+实例一次。

用在哪

比如一些窗体应用程序中,我们希望某些窗体要么不出现,如果出现只能展示一个窗体。还有比如 windows 操作系统的任务管理器,它也只能展示一个窗口。再比如游戏中人物属性界面,不论我们点击多少次,它都展示给用户一个界面。

所以单例模式,它适用于某些类要求只能实例化一次的情况。还有就是外部只能通过一种方式来访问该实例。

优缺点

优点:1.因为只允许创建一个实例,所以可以节约系统资源。
   2.封装性。外部只能通过唯一的入口访问实例,外部受控访问。所以是类控制客户端如何访问,而不是外部想怎么访问就怎么访问。

缺点:1.扩展困难。
   2.违背了“单一职责”原则。因为类不仅需要创建实例,还要控制外部如何访问它。

懒汉式单例 and 饿汉式单例

下面通过一个 winform 窗体实例来演示如何使用单例模式。先建立一个窗体设为父窗体,在父窗体中添加一个菜单,当点击菜单时,出现 FormToolbox 这个窗体。再建立一个名为 FormToolbox 窗体。

单例模式分为:懒汉式和饿汉式。
1)首先来看一下懒汉式单例。

//FormToolbox窗体代码:
public partial class FormToolbox : Form
{
    //将构造函数的修饰符改为私有,使外部无法通过 new 来实例化
    private FormToolbox()
    {
        InitializeComponent();
    }
    //定义一个静态变量,用来判断是否实例化
    private static FormToolbox ftb = null;
    //公有的静态访问方法,唯一的入口
    public static FormToolbox GetInstance()
    {
        if (ftb == null || ftb.IsDisposed)
        {
            ftb = new FormToolbox();
            ftb.MdiParent = Form1.ActiveForm;
        }
        return ftb;
    }
}
//主窗体点击菜单的代码:
private void 工具箱ToolStripMenuItem_Click(object sender, EventArgs e)
{
    FormToolbox.GetInstance().Show();  
}

这就是懒汉式单例:只有窗体需要展示时,才会将自己实例化。即它是一种推迟实例化。
但是它有一个问题,如果是多线程的程序,有可能同时访问类,从而创建多个实例。所以这种情况就需要加锁处理。

2)多线程懒汉式单例

public partial class Form3 : Form
{
    private Form3()
    {
        InitializeComponent();
    }
    private static Form3 instance;
    //程序运行时创建一个静态只读的进程辅助对象
    private static readonly object syncRoot = new object();
    public static Form3 GetInstance()
    {
        //双重锁定
        if (instance == null || instance.IsDisposed)
        {
            lock (syncRoot)
            {
                if (instance == null || instance.IsDisposed)
                {
                    instance = new Form3();
                    instance.MdiParent = Form1.ActiveForm;
                }
            }
        }
        return instance;
    }
}

为什么不直接lock(instance),而是创建一个 syncRoot 呢?
 加锁时,instance实例有没有被创建都不知道,怎么对它加锁呢?看完这句话我也不太明白,所以我就通过代码试了一下,然后就报下面这个错误了。
这里写图片描述
那为什么要用双重否定呢?
 第一次判定instance是否为null,以防止每次调用GetInstance时都需要lock,这样只有instance为null的时候才加锁,可以提高性能。而第二次判定instance是否为空也是必要的,因为有可能出现这样的情况:因为是多线程,所以有可能多个线程同时调用GetInstance的方法,他们都能通过第一次判定,如果不加第二次判定,由于这些线程都认为instance为null,所以会创建多个实例。所以必须加上第二次判定。

3)饿汉式单例

public partial class Form4 : Form
{
    private Form4()
    {
        InitializeComponent();
    }
    //在类中就已经创建了一个实例
    private static readonly Form4 instance = new Form4();
    public static Form4 Getinstance()
    {
        instance.MdiParent = Form1.ActiveForm;
        return instance;
    }        
}

饿汉式单例的代码比较简单,因为饿汉式单例是在类被加载时就已经将自己实例化,所以当外部调用时已经有一个实例,直接返回即可,也不用创建了。饿汉式单例可以不编写安全代码就可以解决多线程的不安全问题,非常方便。

但是我用饿汉式单例的时候遇到一个问题,就是当我们关闭子窗体再重新调用时,它就会报错。
这里写图片描述
如何解决呢?
1.打开窗体的 xxx.Designer.cs代码 2.将 Dispose 方法改为如下形式:

protected override void Dispose(bool disposing)
{
    Hide();
}
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值