51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

【设计模式】CSharp实现创建型模式之-单例模式

前言

单例模式是常用的设计模式之一,单例模式只允许被其自身实例化一次,且向外部提供了一个访问该实例的属性。通常来说,单例对象进行实例化时一般不带参数,因为如果不同的实例化请求传递的参数不同的话会导致问题的产生。(若多个请求都是传递的同样的参数的话,工厂模式更应该被考虑)

C#中实现单例有很多种方法,本文将按顺序介绍懒汉式、饿汉式、Lazy< T > 三种方法

单例模式:保证进程中,类型只有一个实例

  • 构造函数私有化,防止他人实例化
  • 提供一个公开、静态的获取实例的方法
  • 返回共用一个静态字段
  • 类前面加sealed 密封类

加 sealed 关键字

防止继承。在单例模式中,我们希望确保这个类只有一个实例,而且不能被继承

提高性能:sealed 类可以被编译器进行某些优化,因为它知道这个类不会被继承。

明确设计意图:告诉其他开发者,这个类不应该被继承。

单例模式写法

一、懒汉式

什么是懒汉式?懒 代表 只有在第一次使用的时候才会加载,如果不使用那么就不会去加载了

  • 双IF语句判断加锁
//懒汉式 sealed关键字
public sealed class Singleton_lazy
{
    //创建私有静态实例
    private  static Singleton_lazy instance;
    //私有锁
    private static readonly object _lock = new object();

private Singleton_lazy() { }


    //创建静态的、公开的构造实例方法
    public static Singleton_lazy Getinstance()
    {
        if(instance == null)
        {
            lock (_lock)
            {
                if(instance == null)
                {
                    instance = new Singleton_lazy();
                }
            }
        }
        return instance;
    }

    static void Main()
    {
        var instance1 = Singleton_lazy.Getinstance();
        var instance2 = Singleton_lazy.Getinstance();

        //比较两个对象是否相同
        Console.WriteLine(ReferenceEquals(instance1, instance2)); //输出 True
        Console.ReadKey();
    }



`}
`

特点:

  • 只有在首次访问时才创建实例

  • 使用双重检查锁定(double-check locking)来确保线程安全

  • volatile 关键字的作用

  • 内存可见性 :多运用在多线程环境中,当一个字段被声明为 volatile,它确保所有线程在访问该字段时都会直接从主内存读取最新的值,而不是从本地缓存中读取。这意味着一个线程对 volatile 字段的修改对其他线程是立即可见的。

  • readonly关键字的作用

  • 确保字段不可变。保证字段的值在对象创建后不可修改:一旦字段被赋值(在构造函数中),它的值就不能被改变。这对于确保数据一致性和保护对象的状态非常有用。

  • 创建不可变类型:使用 readonly 字段可以帮助实现不可变对象。这些对象在创建后无法更改,因此是线程安全的,适合在多线程环境中使用。

二、饿汉式

  • (推荐)静态字段:由CLR保证,在第一次使用这个类型之前,自动初始化并且只初始化一次
//饿汉式 使用静态字段
public sealed class Singleton_hungry
{
    private Singleton_hungry() { }

    private static readonly Singleton_hungry _singleton_hungry = new Singleton_hungry();

    public static Singleton_hungry GetInstance
    {
        get { return _singleton_hungry; }
    }

    static void Main()
    {
        var instance1 = Singleton_hungry.GetInstance;
        var instance2 = Singleton_hungry.GetInstance;

        //比较两个对象是否相同
        Console.WriteLine(ReferenceEquals(instance1, instance2)); //输出 True
        Console.ReadKey();
    }



`}
`

  • 静态构造函数:由CLR保证,在第一次使用这个类型之前,自动被调用且只调用一次
static SingletonPatternSecond()
{
    _singletonPatternSecond = new SingletonPatternSecond();
}

三、Lazy< T > (推荐使用)

此内容查看价格为2.9lincol代币(包年VIP免费),请先登录
此隐藏内容仅限包年VIP免费查看。包年VIP仅10元,建议升级。VIP可享有哪些特权?

三种方式总结

  • 如果单例的创建和初始化非常耗资源,并且不是每次都需要使用,那么懒汉式更合适。
  • 如果单例的创建和初始化不耗费太多资源,并且每次运行时都会用到,那么饿汉式可能更简单直接。
  • 在现代 .NET 开发中,使用 Lazy 的方式通常是最推荐的,因为它既保证了懒加载,又保证了线程安全,而且实现简单。

单例有什么用?用在哪个场景

同一个实例,不能解决多线程并发问题!会有线程冲突,覆盖数据

单例就是程序只需要这个对象实例化一次

实际应用场景:

  • 唯一序列号
  • 数据库连接池:数据库连接---非托管资源---申请/释放消耗性能。池化资源---内置10个连接---使用来拿,用完放回去避免重复申请和销毁---控制连接数量
  • 线程池
  • 流水号生成器
  • 配置文件读取
  • IOC容器实例

文章参考资料

赞(0)
未经允许不得转载:工具盒子 » 【设计模式】CSharp实现创建型模式之-单例模式