C#设计模式(1)——单例模式(简单版)

目录


单例模式简介

定义

单例模式(Singleton Pattern)是一种创建型设计模式,其目的是确保一个类只有一个实例,并提供全局访问点。这种模式主要用于解决在应用程序中有多个对象实例化时,如何保证只创建一个对象并使得该对象在整个应用中可供访问的需求。

单例模式的关键点是:

  1. 唯一性:类只能有一个实例。
  2. 全局访问点:通过某种方法可以访问到这个实例。
  3. 延迟实例化:类的实例在首次使用时创建,且此实例在整个程序运行期间都保持不变。

单例模式的特点

  • 唯一性:保证类的实例在整个程序生命周期内只有一个。
  • 懒加载:可以在需要时再实例化对象,而不是一开始就创建。
  • 线程安全:确保在多线程环境下,也能正常创建唯一实例,避免竞态条件。

单例模式的应用场景

全局访问点

当一个类需要为全局提供唯一访问时,单例模式是最适合的选择。比如,应用程序中的配置管理器、日志管理器等,它们通常只有一个实例,且需要在整个应用中被多个地方访问。

共享资源管理

在某些情况下,需要对共享资源进行集中管理,如数据库连接池、线程池等。单例模式能有效确保资源管理的唯一性,避免资源重复创建和管理不当。

配置文件管理

系统中的配置信息通常是固定的,但也可能在运行时发生变化。为了方便全局访问配置并保持配置数据的唯一性,可以使用单例模式管理配置文件的读取与写入。


单例模式的实现

在C#中,单例模式可以通过不同的方式实现。下面将分别介绍几种常见的实现方式。

懒汉式

懒汉式是在第一次使用实例时才进行创建,也就是实例的创建是延迟的。懒汉式实现时,可能会出现多线程环境下的并发问题,因此需要在代码中加锁来保证线程安全。

csharpCopy Code
public class SingletonLazy { private static SingletonLazy instance; private static readonly object lockObj = new object(); // 构造函数设为私有,防止外部实例化 private SingletonLazy() { } public static SingletonLazy Instance { get { lock (lockObj) { if (instance == null) { instance = new SingletonLazy(); } return instance; } } } }

饿汉式

饿汉式是在类加载时就创建实例,确保了实例的唯一性。由于实例是在类加载时就创建,因此饿汉式天生是线程安全的。

csharpCopy Code
public class SingletonEager { private static readonly SingletonEager instance = new SingletonEager(); // 构造函数设为私有,防止外部实例化 private SingletonEager() { } public static SingletonEager Instance { get { return instance; } } }

双重锁检查式

双重锁检查式(Double-Checked Locking)是一种优化懒汉式的方式。它通过两次检查 instance 是否为 null 来减少不必要的加锁操作,从而提高性能。

csharpCopy Code
public class SingletonDoubleChecked { private static volatile SingletonDoubleChecked instance; private static readonly object lockObj = new object(); private SingletonDoubleChecked() { } public static SingletonDoubleChecked Instance { get { if (instance == null) { lock (lockObj) { if (instance == null) { instance = new SingletonDoubleChecked(); } } } return instance; } } }

静态内部类

静态内部类的方式是将单例对象的创建放在一个静态内部类中,由JIT(即时编译器)保证线程安全。此方法利用了类加载机制来确保唯一性,同时也避免了锁的开销。

csharpCopy Code
public class SingletonStaticInner { private SingletonStaticInner() { } public static SingletonStaticInner Instance { get { return SingletonHolder.Instance; } } private class SingletonHolder { static SingletonHolder() { } internal static readonly SingletonStaticInner Instance = new SingletonStaticInner(); } }

单例模式的优缺点

优点

  • 节省内存:单例模式可以避免对象的重复创建,节省系统内存。
  • 控制实例化次数:通过确保只有一个实例,单例模式能够避免资源的浪费。
  • 全局访问:提供全局访问点,便于其他对象进行访问。
  • 线程安全:通过合理设计,单例模式能够确保在多线程环境下的线程安全。

缺点

  • 隐藏类的具体实现:单例模式将构造函数设置为私有,可能会导致某些情况下,单元测试变得更加困难。
  • 不适应频繁变更:单例模式一旦实现就难以改变,对于业务需求发生变更时,修改起来相对较为麻烦。
  • 过度依赖全局实例:由于单例模式提供的是全局访问点,过度使用可能导致代码之间强耦合,使得代码的维护性和扩展性变差。

单例模式的使用案例

配置管理器实例

在大多数应用程序中,我们需要一个统一的配置管理器来加载配置信息并全局共享这些配置。例如,一个Web应用程序的配置文件可能包含数据库连接字符串、API密钥、日志级别等。

使用单例模式,我们可以确保配置管理器的实例在整个程序生命周期内只有一个,并且可以随时访问到配置数据。

csharpCopy Code
public class ConfigurationManager { private static ConfigurationManager instance; private static readonly object lockObj = new object(); public string DbConnectionString { get; private set; } public string ApiKey { get; private set; } private ConfigurationManager() { // 从文件或数据库加载配置信息 DbConnectionString = "Data Source=localhost;Initial Catalog=MyDb;Integrated Security=True"; ApiKey = "abcdef123456"; } public static ConfigurationManager Instance { get { lock (lockObj) { if (instance == null) { instance = new ConfigurationManager(); } return instance; } } } }

在应用程序中任何地方需要配置数据时,只需要通过 ConfigurationManager.Instance 访问即可。

日志管理器实例

另一个常见的单例模式应用场景是日志管理器。在大多数应用程序中,日志记录通常是全局唯一的,且在多个地方都会访问到。例如,应用程序中的错误日志、调试日志、信息日志等都需要集中管理。

csharpCopy Code
public class Logger { private static Logger instance; private static readonly object lockObj = new object(); private Logger() { } public static Logger Instance { get { lock (lockObj) { if (instance == null) { instance = new Logger(); } return instance; } } } public void Log(string message) { // 记录日志 Console.WriteLine($"{DateTime.Now}: {message}"); } }

通过单例模式,日志管理器可以确保日志写入是集中管理的,且在应用程序中始终使用相同的实例。


总结

单例模式是一种常用的设计模式,广泛应用于需要全局唯一实例的场景。它通过控制类的实例化过程,确保了应用程序中该类只有一个实例,并提供全局访问点。无论是配置管理、日志管理,还是其他资源共享,