C#中的单例模式

8,978次阅读
没有评论

共计 2925 个字符,预计需要花费 8 分钟才能阅读完成。

C# 中的单例模式


文章目录

  • C# 中的单例模式
  • 一、什么是单例模式?
  • 二、单例模式的实现方式
    • 1. 通过静态字段实现
    • 2. 通过属性的 get 实现
    • 3. 通过 Lazy 实现单例模式
    • 4. 通过 DependencyInjection
  • 总结

一、什么是单例模式?

单例模式是一种确保一个类只有一个实例的 设计模式。比如在各种工具类中,每次需要访问这个类的方法时,都实例化一个实例,这是明显不合适的

二、单例模式的实现方式

1. 通过静态字段实现

代码如下(示例):

public class Singleton
{
	public static readonly Singleton Instance = new();
	private Singleton()
	{
	}
}

上述是最简单而且是以线程安全的方式实现的单例模式
1. 利用静态字段确保访问 Instance 时,不会重复实例化单例类,这是利用静态字段只会初始化一次的特性来实现的。
2.readonly 字段确保了外部使用该实例时无法更改该 Instance,例如Singleton.Instance=null 是报错的。
3. 私有构造函数确保无法在外部实例化该类,例如 new Singleton() 是报错的。
不过上述方法有两个弊端:

缺点 1 :不符合字段的命名方式,字段命名的方式一般有三种,比如 instance_instance 或者 m_instance
这个缺点可以很好的进行规避,再加一个对应的属性,并将该字段的访问级别更改为 private 即可。

代码如下:

public class Singleton
{
	
	private static readonly Singleton _instance = new();
	
	public static Singleton Instance
	{
		get => _instance;
	}
	private Singleton()
	{
	}
}

缺点 2 :无法彻底实现懒加载

  • 如果当上述单例类中,只存在一个 Instance 静态属性和 _instance 字段,再没有其他静态成员时,是可以实现懒加载的。

  • 这是由于静态成员的特点所决定的,当一个类中存在多个静态成员时,第一次访问任意一个静态成员,都会导致所有的静态成员被初始化。但是当我们确保一个类中只有一个可被访问的静态成员,也可以实现懒加载

  • 由于这种方法存在限制,所以一般不使用这种方式实现懒加载

2. 通过属性的 get 实现

代码如下(示例):

public class Singleton
{
	
    private static Singleton _instance;
    public static Singleton Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new Singleton();
            }
            return _instance;
        }
    }
    private Singleton()
    {
    }
}

上述方式虽然实现了懒加载,但是无法确保线程安全
实现线程安全的方式很简单,加入 lock 线程锁:

public class Singleton
{
    private static Singleton _instance;
    private static readonly object _lock = new();

    public static Singleton Instance
    {
        get
        {
            lock (_lock)
            {
                if (_instance == null)
                {
                    _instance = new Singleton();
                }
            }
            return _instance;
        }
    }

    private Singleton()
    {
        Console.WriteLine("构造函数初始化");
    }
}

这种方式完美的实现了单例以及懒加载,但是仍然有一个缺点,就是每次访问该属性时,不论_instance 是否被实例化,都会访问 lock 块,导致不必要的资源开销,所以我们在 lock 之前先判断一下_instance 是否被实例化,如果已被实例化,则直接返回,这就是 双检查 实现单例。

代码如下(示例):

public class Singleton
{
    private static Singleton _instance;
    private static readonly object _lock = new();

    public static Singleton Instance
    {
        get
        {
        	
        	if (_instance == null)
            {
            	
               lock (_lock)
	            {
	                if (_instance == null)
	                {
	                    _instance = new Singleton();
	                }
	            }
            }
            
            return _instance;
        }
    }
    private Singleton()
    {
    }
}

通过双检查实现的单例模式即实现了懒加载,同时又保证了线程安全,但是还有没有更简洁的方式来实现单例呢,下面进一步探讨

3. 通过 Lazy 实现单例模式

代码如下:

public class Singleton
{
	
    private static readonly LazySingleton> _instance = new LazySingleton>(() => new Singleton(),true);
    public static Singleton Instance { get => _instance.Value; }
    private Singleton()
    {
    }
}

可以看到通过使用 Lazy,代码相比双检查,不再需要手动编写锁定代码,而且 Lazy 提供了工厂方法,可以在工厂方法中更灵活的初始化实例,比如 ()=>new Singleton{Prop1 = 1,...},并且能灵活配置是否需要线程安全。
通过以上方式我们实现了非常简洁的单例实现,同时保证了线程安全以及懒加载,那么还有没有其他方式?

4. 通过 DependencyInjection

代码如下:

using Microsoft.Extensions.DependencyInjection;

var serviceCollection = new ServiceCollection();

serviceCollection.AddSingletonService>();
var serviceProvider = serviceCollection.BuildServiceProvider();

var service = serviceProvider.GetServiceService>();
service.Func();

public class Service
{
    public void Func()
    {
        Console.WriteLine("Hello World");
    }
}

DI 容器通过提供 AddSingleton 这种方式实现了类的生命周期管理,表示在整个生命周期中该实例都是单例,同时 AddSingleton 还提供了多个重载,比如提供工厂方法的重载,可以调用不同的构造方法,比如AddSingleton((provider)=>new Service(param1,...))

总结

通过上述实现单例模式的各种方法的探讨,我们还需要在实际开发过程中,根据不同的需求来进行选择不同的实现方式。比如一个比较小的项目,后续改动不大,可以通过第一种 public static readonly Singleton Instance = new(); 来实现;比如我们开发 ASP.Net,这时我们就需要用到 DependencyInjection 这种方式

原文地址: C# 中的单例模式

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-11-07发表,共计2925字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)