c#单例模式

一、设计模式与单例模式基础

1. 设计模式的本质

设计模式是解决软件开发特定问题的成熟解决方案 ,核心目标是通过规范化的设计思路,实现代码的高内聚 (模块专注单一功能)和低耦合(减少模块间依赖),从而提升代码的可重用性、可维护性和灵活性。

2. 高内聚与低耦合
  • 高内聚:一个模块(类 / 组件)应专注于完成单一功能,类似公司中 "部门各司其职"(如财务部只负责财务,人事部只负责人事),避免功能混杂。

  • 低耦合:模块间的依赖应尽可能少,交互通过明确接口进行,类似公司部门间通过负责人对接,而非直接跨部门干预,确保模块可独立移植或修改。

代码示例:

cs 复制代码
// 1. 饿汉式单例(线程安全,但可能提前初始化)
public sealed class EagerSingleton
{
    // 静态构造函数会在第一次使用类时调用
    private static readonly EagerSingleton instance = new EagerSingleton();
    
    // 私有构造函数阻止外部实例化
    private EagerSingleton() { }
    
    // 提供全局访问点
    public static EagerSingleton Instance
    {
        get { return instance; }
    }
    
    // 单例类的其他方法
    public void DoSomething()
    {
        Console.WriteLine("EagerSingleton is doing something.");
    }
}

// 2. 懒汉式单例(双重锁定检查,线程安全且延迟初始化)
public sealed class LazySingleton
{
    // 声明为volatile确保多线程环境下的可见性
    private static volatile LazySingleton instance;
    private static readonly object syncRoot = new object();
    
    private LazySingleton() { }
    
    public static LazySingleton Instance
    {
        get
        {
            // 第一次检查,避免每次都加锁
            if (instance == null)
            {
                // 加锁确保线程安全
                lock (syncRoot)
                {
                    // 第二次检查,防止多个线程同时通过第一次检查
                    if (instance == null)
                    {
                        instance = new LazySingleton();
                    }
                }
            }
            return instance;
        }
    }
    
    public void DoSomething()
    {
        Console.WriteLine("LazySingleton is doing something.");
    }
}

// 3. 使用Lazy<T>实现(.NET 4.0及以上推荐方式)
public sealed class ModernSingleton
{
    // 使用Lazy<T>自动实现延迟初始化和线程安全
    private static readonly Lazy<ModernSingleton> lazyInstance = 
        new Lazy<ModernSingleton>(() => new ModernSingleton());
    
    public static ModernSingleton Instance
    {
        get { return lazyInstance.Value; }
    }
    
    private ModernSingleton() { }
    
    public void DoSomething()
    {
        Console.WriteLine("ModernSingleton is doing something.");
    }
}

// 4. 静态内部类实现(线程安全且延迟初始化)
public sealed class NestedSingleton
{
    // 私有构造函数
    private NestedSingleton() { }
    
    // 静态内部类
    private static class SingletonHolder
    {
        // 当第一次访问NestedSingleton.Instance时才会初始化
        internal static readonly NestedSingleton instance = new NestedSingleton();
    }
    
    public static NestedSingleton Instance
    {
        get { return SingletonHolder.instance; }
    }
    
    public void DoSomething()
    {
        Console.WriteLine("NestedSingleton is doing something.");
    }
}

// 使用示例
public class SingletonDemo
{
    public static void Main()
    {
        // 使用饿汉式单例
        EagerSingleton eager = EagerSingleton.Instance;
        eager.DoSomething();
        
        // 使用懒汉式单例
        LazySingleton lazy = LazySingleton.Instance;
        lazy.DoSomething();
        
        // 使用现代Lazy<T>单例
        ModernSingleton modern = ModernSingleton.Instance;
        modern.DoSomething();
        
        // 使用静态内部类单例
        NestedSingleton nested = NestedSingleton.Instance;
        nested.DoSomething();
        
        // 验证单例唯一性
        EagerSingleton eager2 = EagerSingleton.Instance;
        Console.WriteLine($"Eager instances are the same: {eager == eager2}");
    }
}

这些单例模式实现各有特点:

  1. 饿汉式:实现简单,线程安全,但会在类加载时就初始化实例,可能造成资源浪费

  2. 懒汉式(双重锁定):实现了延迟初始化,通过双重检查锁定保证线程安全,在多线程环境下性能较好

  3. Lazy<T>实现这是.NET 4.0 及以上版本推荐的方式,利用框架内置的 Lazy<T>类型,既保证线程安全又实现延迟初始化,代码简洁

  4. 静态内部类:巧妙利用 C# 的类加载机制实现延迟初始化和线程安全,性能优秀

在实际开发中,推荐使用Lazy<T>方式实现单例,它是框架提供的线程安全实现,代码简洁且不易出错。所有这些实现都通过私有构造函数确保类不能被外部实例化,并提供了一个全局访问点Instance属性。

二、单例模式的核心定义与目标

1. 官方定义

确保一个类只有一个实例,并提供一个全局访问点。

  • 核心要素:

    • 唯一性:无论何时何地访问,该类只能创建一个实例(避免重复创建导致的资源冲突或浪费)。

    • 全局访问:提供统一的入口获取该实例,方便系统中任何地方调用。

2. 为何需要单例模式?

当系统中某个对象的多实例会导致问题时,单例模式是最优解。典型场景包括:

  • 资源独占场景:如操作系统的 "任务管理器"(只能打开一个窗口,避免多实例抢占系统资源)。

  • 状态一致性场景:如配置管理器(全局配置需统一,多实例可能导致配置不一致)。

  • 高频访问且资源密集型对象:如数据库连接池(重复创建连接会消耗大量资源,单例可复用连接)。

三、单例模式与静态类的核心区别

单例模式(实例对象)与静态类(静态方法集合)常被混淆,但二者在设计目的和特性上有本质区别,具体对比如下:

对比维度 单例模式 静态类
本质 一个全局唯一的对象(可实例化,存在对象状态) 一组静态方法的集合(不可实例化,无对象状态)
继承与多态 可继承父类,方法可被override(支持面向对象特性) 不可继承,静态方法无法被override(不支持面向对象特性)
初始化时机 支持懒加载(首次使用时才创建实例,节省资源) 编译期初始化(程序启动时即加载,无法延迟)
状态维护 可维护对象状态(如内部字段值的变更) 无状态(仅通过静态字段维护数据,易引发线程安全问题)
性能 方法调用为运行时绑定(略慢) 方法调用为编译期绑定(更快)
适用场景 需维护状态、需面向对象特性(继承 / 多态)的场景 仅提供工具方法(无状态操作),如数学计算、字符串工具类

四、单例模式的实现核心原则

(即使未提供代码,实现单例模式需遵循以下原则以保证 "唯一性"):

  1. 私有构造函数 :禁止外部通过new创建实例(核心约束)。

  2. 静态私有实例:类内部维护唯一实例(确保全局唯一)。

  3. 公共静态方法 :提供全局访问点(如GetInstance()),返回唯一实例。

五、总结:单例模式的核心价值

单例模式是 "唯一性" 与 "灵活性" 的平衡方案

  • 解决了 "全局唯一实例" 的问题(避免多实例冲突);

  • 保留了面向对象的特性(继承、多态、状态维护),比静态类更灵活;

  • 适用于需全局访问且需唯一实例的场景(如资源管理器、配置中心等)。

理解单例模式的关键在于:何时需要 "唯一实例",以及为何静态类无法满足这类场景的面向对象需求