C#设计模式之单例模式的一些思考和改进

单例模式


前言

单例是一种创建型设计模式,让你能够保证一个类只有一个实例,并提供一个访问该实例的全局节点。

一、抛出问题

1.我们为什么要使用单例模式?

2.为什么不用静态类来代替单例模式呢?

3.多线程下如何让单例模式变得安全

这三个问题是我们在使用单例模式时最常见的问题,为什么要用?什么时候使用?为什么不能用静态类代替单例模式?他俩的区别在哪里呢?什么时候用静态类好,什么时候用单例模式好?多线程下单例模式为什么不安全,怎么样才能让他变安全?


二、回答问题


1.为什么要使用单例模式

单例模式的优点和使用场景:
  • 在内存中只有一个实例对象,节省内存空间。
  • 避免重复的创建和销毁对象,可以提高性能,避免对多重资源的重复占用,可以全局进行访问
  • 需要频繁的实例化和销毁的对象。有状态的工具类对象,频繁访问数据库或文件对象.

根据这些不难看出,单例模式的诞生,是为了节省内存空间,避免多次重复创建和销毁对象。如果那个对象占用内存少,多次创建和销毁对于运行影响不大,但是一个很大很大的对象被重复,就会浪费内存空间,降低效率。


2.为什么不用静态类来代替单例模式

要说清楚这个问题实在很难,所以我们先搞清楚静态类和非静态的区别,然后再解释这个问题

(1) 静态方法和非静态方法的区别

在C#中,使用Stack和托管堆Heap进行分配管理。而托管堆最主要的部分为GC Heap垃圾回收堆,由GC所管理,另一部分为Loader Heap加载堆,而Loader Heap最重要的信息是元数据相关信息,在堆中体现为Method Table方法表。Method Table则记录了存储的元数据信息。
在这里插入图片描述

请注意,所有方法都保存在Method Table中,无论是静态还是非静态,均保存在Loader Heap中。
在这里插入图片描述
在调用上,静态方法要比非静态方法快一点,因为非静态方法要进行实例化。


(2) 为什么要有非静态方法

那么从上面来看,非静态的速度比较慢,不如静态方法好使,为什么还要使用非静态方法呢?

答: 为了让开发更加模式化、面向对象化。这样说的话,静态方法和实例化方式的区分是为了解决模式的问题。面向对象,需要的继承和多态,都是非静态方法。


(3)什么时候使用静态类,什么时候使用单例

首先先看他们之间的优劣:

  • 静态类比单例具有更好的性能,因为静态方法在编译期绑定。
  • 静态方法无法被override,但非静态方法的单例模式则可以依赖继承覆盖方法。
  • 如果你的需求中需要维护状态信息,则单例比静态类更适合,因为后者在维护状态信息方面是非常可怕的,并导致狡滑的 bug。

简单的说,当不需要维护任何状态,仅仅提供全局访问的方法时,使用静态类;但需要进行面向对象的开发时,使用单例。


3.多线程下如何让单例模式变得安全

(1)单线程下的单例模式

 class HelloWorld
    {
        private static HelloWorld instance;
        private HelloWorld() { }
        public static HelloWorld Instance
        {
            get
            {
                if (instance == null)
                    instance = new HelloWorld();
                return instance;
            }
        }
        public void show()=> Console.WriteLine("单例设计模式");
    }
调用:
			HelloWorld helloWorld=HelloWorld.Instance;
            helloWorld.show();

在这里插入图片描述

 if (instance == null)
	 instance = new HelloWorld();
 return instance;

这段看样子是没什么错误,但是这仅在单线程下,在多线程下,如果两个线程都访问if语句,那么肯定都是null,那么两个线程会各自创建一个对象。

(2)多线程下的改进

 private static volatile HelloWorld instance=null;//不被编译器所排序
        /// <summary>
        /// 辅助器,本身不参与构建 
        /// </summary>
        private static object lockHelper=new object();
        private HelloWorld() { }
        public static HelloWorld Instance
        {
            get
            {
                if (instance == null)
                    lock (lockHelper)
                        if (instance==null)//双检查
                            instance=new HelloWorld();
                return instance;
            }
        }

极简单版:

 		private HelloWorld() { }
        public static readonly HelloWorld Instance=new HelloWorld();//内联初始化
        
		等同于
		
		public static readonly HelloWorld Instance;
        static HelloWorld() => Instance = new HelloWorld();

三、总结

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值