csharp:gof23种设计模式:单例模式

C# 单例模式 (Singleton Pattern)

单例模式是 C# 开发中最常用的设计模式之一。它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。

在 C# 应用程序中,单例模式通常用于管理共享资源,例如:

  • 数据库连接池
  • 日志记录器 (Logger)
  • 配置文件管理器 (Configuration Manager)
  • 硬件接口访问(如打印机后台处理程序)

要实现一个标准的单例类,必须满足以下条件:

  1. 私有构造函数:防止外部通过 `new` 关键字创建实例。
  2. 私有静态变量:用于持有类的唯一实例。
  3. 公共静态方法/属性:提供全局访问点(通常命名为 `Instance`)。

在 C# 中实现单例有多种方式,主要区别在于线程安全性延迟加载 (Lazy Loading) 的处理。

这是最基本的实现,但在多线程环境下是不安全的。如果两个线程同时判断 `_instance == null`,可能会创建两个实例。

public class Singleton
{
    private static Singleton _instance;
 
    // 私有构造函数
    private Singleton() { }
 
    public static Singleton Instance
    {
        get
        {
            // 线程不安全!
            if (_instance == null)
            {
                _instance = new Singleton();
            }
            return _instance;
        }
    }
}

通过 `lock` 关键字确保同一时间只有一个线程执行创建实例的代码。

public class Singleton
{
    private static Singleton _instance;
    private static readonly object _lock = new object();
 
    private Singleton() { }
 
    public static Singleton Instance
    {
        get
        {
            // 每次访问都会加锁,性能较差
            lock (_lock)
            {
                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;
        }
    }
}

利用 .NET CLR 的特性,静态构造函数只会执行一次。这种方式非常简单且线程安全,但缺点是不支持延迟加载(类一加载,实例就被创建,即使你还没用到它)。

public sealed class Singleton
{
    // CLR 保证了静态字段初始化的线程安全性
    private static readonly Singleton _instance = new Singleton();
 
    // 显式静态构造函数告诉 C# 编译器不要将类型标记为 BeforeFieldInit
    static Singleton() { }
 
    private Singleton() { }
 
    public static Singleton Instance
    {
        get
        {
            return _instance;
        }
    }
}

在 .NET 4.0 及更高版本中,这是最推荐的实现方式。它利用 `System.Lazy<T>` 类型自动处理线程安全和延迟加载,代码简洁且高效。

public sealed class Singleton
{
    // Lazy<T> 默认是线程安全的
    private static readonly Lazy<Singleton> _lazy = 
        new Lazy<Singleton>(() => new Singleton());
 
    private Singleton() { }
 
    public static Singleton Instance 
    { 
        get 
        { 
            return _lazy.Value; 
        } 
    }
}
实现方式 线程安全 延迟加载 复杂度 推荐指数
简单实现 No Yes 不推荐
简单加锁 Yes Yes 一般
双重检查锁定 Yes Yes 一般
静态初始化 Yes No 推荐 (如果不需要延迟加载)
System.Lazy<T> Yes Yes 强烈推荐
  1. 单一职责原则:单例类除了管理自己的实例外,通常还承担具体的业务功能。要注意不要让单例类变得过于臃肿。
  2. 测试困难:单例模式通常持有全局状态,这使得单元测试变得困难(难以模拟和重置状态)。在需要高度可测试性的系统中,通常建议使用依赖注入 (Dependency Injection) 容器来管理单例的生命周期,而不是手动实现单例模式。
请输入您的评论. 可以使用维基语法:
 

该主题尚不存在

您访问的页面并不存在。如果允许,您可以使用创建该页面按钮来创建它。

  • csharp/gof23种设计模式/单例模式.txt
  • 最后更改: 2025/12/29 11:22
  • 张叶安