C# 一分钟浅谈:设计模式之单例模式

本文涉及的产品
可观测监控 Prometheus 版,每月50GB免费额度
可观测可视化 Grafana 版,10个用户账号 1个月
云原生网关 MSE Higress,422元/月
简介: 【10月更文挑战第9天】单例模式是软件开发中最常用的设计模式之一,旨在确保一个类只有一个实例,并提供一个全局访问点。本文介绍了单例模式的基本概念、实现方式(包括饿汉式、懒汉式和使用 `Lazy<T>` 的方法)、常见问题(如多线程和序列化问题)及其解决方案,并通过代码示例详细说明了这些内容。希望本文能帮助你在实际开发中更好地应用单例模式,提高代码质量和可维护性。

在软件开发中,设计模式是一种被广泛接受的最佳实践,用于解决特定问题或实现特定功能。单例模式(Singleton Pattern)是其中最简单也是最常用的设计模式之一。本文将从单例模式的基本概念出发,逐步深入探讨其实现方式、常见问题、易错点及如何避免这些问题,并通过代码示例进行详细说明。
image.png

单例模式的基本概念

单例模式的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式通常用于那些需要频繁创建和销毁的对象,或者那些在整个应用程序生命周期中只需要一个实例的对象。

优点

  • 资源消耗低:由于整个应用程序中只有一个实例,因此可以节省内存。
  • 全局访问:提供了一个全局访问点,方便在任何地方使用该实例。
  • 控制共享资源:可以更好地控制对共享资源的访问,例如数据库连接、线程池等。

缺点

  • 滥用单例:如果过度使用单例模式,可能会导致代码耦合度增加,难以测试和维护。
  • 多线程问题:在多线程环境中,如果不加锁处理,可能会导致多个实例的创建。

单例模式的实现方式

饿汉式(Eager Initialization)

饿汉式是最简单的单例模式实现方式,它在类加载时就创建了实例。这种方式是线程安全的,但可能会浪费资源,因为实例在程序启动时就被创建了,即使不使用也会占用内存。

public class Singleton
{
   
    // 在静态构造函数中创建实例
    private static readonly Singleton _instance = new Singleton();

    // 私有构造函数,防止外部实例化
    private Singleton() {
    }

    // 提供全局访问点
    public static Singleton Instance
    {
   
        get {
    return _instance; }
    }
}

懒汉式(Lazy Initialization)

懒汉式在第一次使用时才创建实例,这种方式可以节省资源,但需要处理多线程问题。

线程不安全的懒汉式

public class Singleton
{
   
    private static Singleton _instance;

    private Singleton() {
    }

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

线程安全的懒汉式

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

    private Singleton() {
    }

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

使用 Lazy<T> 实现线程安全的懒汉式

Lazy<T> 是 .NET 框架提供的一个类,可以方便地实现线程安全的懒汉式单例。

public class Singleton
{
   
    private static readonly Lazy<Singleton> _lazy = new Lazy<Singleton>(() => new Singleton());

    private Singleton() {
    }

    public static Singleton Instance
    {
   
        get {
    return _lazy.Value; }
    }
}

常见问题与易错点

多线程问题

在多线程环境中,如果不加锁处理,可能会导致多个实例的创建。如上所述,可以通过双检锁(Double-Check Locking)或使用 Lazy<T> 来解决这个问题。

序列化问题

在某些情况下,单例对象可能需要被序列化和反序列化。如果直接序列化和反序列化单例对象,可能会导致多个实例的创建。可以通过实现 ISerializable 接口来解决这个问题。

[Serializable]
public class Singleton : ISerializable
{
   
    private static readonly Singleton _instance = new Singleton();
    private static readonly object _lock = new object();

    private Singleton() {
    }

    public static Singleton Instance
    {
   
        get {
    return _instance; }
    }

    protected Singleton(SerializationInfo info, StreamingContext context)
    {
   
        // 反序列化时返回现有的实例
        _instance = (Singleton)info.GetValue("Instance", typeof(Singleton));
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
   
        info.AddValue("Instance", _instance);
    }
}

反射问题

通过反射,可以在运行时创建私有构造函数的实例,从而破坏单例模式。可以通过在构造函数中添加检查来防止这种情况。

public class Singleton
{
   
    private static readonly Singleton _instance = new Singleton();
    private static bool _isInitialized = false;

    private Singleton()
    {
   
        if (_isInitialized)
        {
   
            throw new InvalidOperationException("Singleton instance already created.");
        }
        _isInitialized = true;
    }

    public static Singleton Instance
    {
   
        get {
    return _instance; }
    }
}

总结

单例模式是一种简单但强大的设计模式,适用于需要全局唯一实例的场景。通过本文的介绍,我们了解了单例模式的基本概念、实现方式、常见问题及解决方案。希望这些内容能帮助你在实际开发中更好地应用单例模式,提高代码的质量和可维护性。

如果你有任何疑问或建议,欢迎在评论区留言交流。感谢阅读!

目录
相关文章
|
4月前
|
设计模式 缓存 安全
【设计模式】【创建型模式】单例模式(Singleton)
一、入门 什么是单例模式? 单例模式是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。它常用于需要全局唯一对象的场景,如配置管理、连接池等。 为什么要单例模式? 节省资源 场景:某些对象创
162 15
|
11月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
86 2
|
5月前
|
设计模式 存储 安全
设计模式-单例模式练习
单例模式是Java设计模式中的重要概念,确保一个类只有一个实例并提供全局访问点。本文详解单例模式的核心思想、实现方式及线程安全问题,包括基础实现(双重检查锁)、懒汉式与饿汉式对比,以及枚举实现的优势。通过代码示例和类图,深入探讨不同场景下的单例应用,如线程安全、防止反射攻击和序列化破坏等,展示枚举实现的简洁与可靠性。
106 0
|
6月前
|
设计模式 安全 Java
设计模式:单例模式
单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供全局访问点。它通过私有化构造函数、自行创建实例和静态方法(如`getInstance()`)实现。适用于数据库连接池、日志管理器等需要全局唯一对象的场景。常见的实现方式包括饿汉式、懒汉式、双重检查锁、静态内部类和枚举。线程安全问题可通过`synchronized`或双重检查锁解决,同时需防止反射和序列化破坏单例。优点是避免资源浪费,缺点是可能增加代码耦合度和测试难度。实际开发中应优先选择枚举或静态内部类,避免滥用单例,并结合依赖注入框架优化使用。
|
7月前
|
设计模式 存储 安全
设计模式2:单例模式
单例模式是一种创建型模式,确保一个类只有一个实例,并提供全局访问点。分为懒汉式和饿汉式: - **懒汉式**:延迟加载,首次调用时创建实例,线程安全通过双重检查锁(double check locking)实现,使用`volatile`防止指令重排序。 - **饿汉式**:类加载时即创建实例,线程安全但可能浪费内存。 示例代码展示了如何使用Java实现这两种模式。
116 4
|
9月前
|
设计模式 存储 前端开发
前端必须掌握的设计模式——单例模式
单例模式是一种简单的创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。适用于窗口对象、登录弹窗等场景,优点包括易于维护、访问和低消耗,但也有安全隐患、可能形成巨石对象及扩展性差等缺点。文中展示了JavaScript和TypeScript的实现方法。
351 13
|
9月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
105 2
|
10月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
138 4
|
10月前
|
设计模式 安全 C#
C# 单例模式的多种实现
C# 单例模式的多种实现
|
10月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式