C#设计模式之单例模式

这篇具有很好参考价值的文章主要介绍了C#设计模式之单例模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

介绍

单例模式(Singleton)保证一个类仅有一个实例,并提供一个访问它的全局访问点。

单例模式的结构图如下所示:

使用单例模式的原因

对一些类来说,只有一个实例是很重要的。如何才能保证一个类只有一个实例并且这个实例易于被访问呢?

基于程序员之间的约定或是利用全局变量吗?

虽然这样或许也可以实现,但是单例模式确是更好的做法。因为仅仅靠约定或是全局变量,如果重新new一个对象,还是可以创建新的实例。

经典的单例模式

现在我们先来看看经典的单例模式写法:

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

声明了一个静态的类变量,静态类变量在类的所有实例之间共享, 与类关联而不是与类的实例关联,它在整个应用程序域中只有一个实例。

 private Singleton() { }

私有的构造函数,这使得我们无法通过:

 Singleton Singleton = new Singleton();

这种写法来创建它的实例,这样写会报错,如下所示:

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

该方法是获得本类实例的唯一全局访问点。

如果实例不存在,在类的内部可以通过new操作符来获取一个实例,否则返回已有的实例。

 internal class Program
{
    static void Main(string[] args)
    {        
        Singleton singleton1 = Singleton.GetInstance();
        Singleton singleton2 = Singleton.GetInstance();
        if (singleton1 == singleton2)
            Console.WriteLine("两个对象是相同的实例");
    }
}

比较两个对象看看它们是不是同一个实例,运行结果如下所示:

多线程下的单例模式

上述代码在多线程情况下会存在问题,如果有多个线程同时访问Singleton类调用GetInstance方法,会有可能造成创建多个实例。

查看以下代码:

internal class Program
{
   static void Main(string[] args)
  {        
       // 创建并启动两个任务
       Task task1 = Task.Factory.StartNew(Method1);
       Task task2 = Task.Factory.StartNew(Method2);
       Task.WaitAll(task1, task2);        
       Console.WriteLine("所有任务完成");
       void Method1()
      {
           Console.WriteLine("Task 1 is running.");
           Task.Delay(1000).Wait();
           Singleton singleton1 = Singleton.GetInstance();
           Console.WriteLine(singleton1.GetHashCode());
      }

        void Method2()
      {        
           Console.WriteLine("Task 2 is running.");
           Task.Delay(1000).Wait();
           Singleton singleton2 = Singleton.GetInstance();
           Console.WriteLine(singleton2.GetHashCode());
      }
  }
}

运行结果如下所示:

两个对象的哈希码不同,在C#中,每个对象都有一个GetHashCode方法,该方法返回该对象的哈希码。默认情况下,GetHashCode方法是根据对象的内存地址生成的,因此两个不同的实例在内存中有不同的地址,它们的哈希码通常也是不同的。然而,这并不是说哈希码不同就一定表示两个对象是不同的。因为哈希码是一个有限的整数,存在哈希冲突的可能性。两个不同的对象可能具有相同的哈希码,这被称为哈希冲突。因此,不能仅仅通过比较哈希码就断定两个对象是相同的还是不同的。

但是我们在这里我们可以这样进行简单的判断。

那么该如何解决这个问题呢?

使用lock

C#中可以使用lock来解决。 lock 语句可确保在任何时候最多只有一个线程执行其主体。

可以这样进行改写:

 public class Singleton
{
    private static Singleton? _instance;
    private static readonly object _syncRoot = new object();
    private Singleton() { }
    public static Singleton GetInstance()
    {
        lock (_syncRoot)
        {
            if (_instance == null)
                _instance = new Singleton();
        }
       return _instance;
    }
}
 private static readonly object _syncRoot = new object();

程序运行时创建一个静态只读的辅助对象。

  public static Singleton GetInstance()
    {
        lock (_syncRoot)
        {
            if (_instance == null)
                _instance = new Singleton();
        }
       return _instance;
    }

lock语句确保在任何时候最多只有一个线程执行其主体。

现在再来看看运行结果:

双重锁定

现在两个对象的哈希码一样了,在这里我们可以简单的认为是同一个实例了。

但是每次都要执行lock语句会影响性能,还需要改进:

  public class Singleton
{
     private static Singleton? _instance;
     private static readonly object _syncRoot = new object();
     private Singleton() { }
     public static Singleton GetInstance()
    {
         if (_instance == null)
        {
             lock (_syncRoot)
            {
                 if(_instance == null)
                {
                     _instance = new Singleton();
                }
            }
        }              
        return _instance;
    }
}

这样子当存在_instance时,直接返回,没有执行lock语句。为什么执行两次if(_instance == null)判断是因为当_instance == null时,如果同时有两个线程调用GetInstance方法,它们都可以通过第一次判断,由于lock的机制,这两个线程只有一个进去,另一个在外排队等候,必须等上一个进入并且退出之后,第二个线程才能进入,而此时如果没有第二层的判断,那么第一个线程与第二个线程都会创建新的实例,而添加了这个判断之后,第一个线程创建了实例,_instanc就不为空,第二个线程就无法创建新的实例了。

现在再来看看运行结果:

静态初始化

其实在实际应用中,C#也可以采用静态初始化的方法,这种方法不需要开发人员显式地编写线程安全代码,即可解决多线程环境下不安全的问题。

 public sealed class Singleton
{
    private static readonly Singleton _instance = new Singleton();    
    private Singleton() { }
    public static Singleton GetInstance()
    {        
      return _instance;
    }
}

使用sealed关键字表示是密封类,阻止发生派生,因为派生可能会增加实例。

 private static readonly Singleton _instance = new Singleton();     

在第一次引用类的任何成员时创建实例,公共语言运行时负责处理变量初始化。

现在再来看看运行结果:

由于这种静态初始化的方式是在自己被加载时就将自己实例化,所以被形象地称之为饿汉单例类,原先的单例模式的处理方式是要在第一次被引用时,才会将自己实例化,所以就被称为懒汉单例类

总结

本文介绍了在C#中如何使用单例模式,并介绍了在多线程模式下单例模式可能存在的问题及其解决方法,希望对你有所帮助。

参考

1、《Head First 设计模式(中文版)》

2、《大话设计模式》

3、《设计模式:可复用面向对象软件的基础》文章来源地址https://www.toymoban.com/news/detail-777750.html

到了这里,关于C#设计模式之单例模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • c++设计模式之单例模式

    一个类无论创建多少对象 , 都只能得到一个实例 如上述代码中,我们通过new运算符创建出了类A的三个对象实例,而我们现在要做的是,如何设计类A,使得上述代码运行之后永远只产生同一个对象实例         我们知道,一个类对象是通过这个类的构造函数创建的,因此

    2024年01月19日
    浏览(38)
  • Unity设计模式之单例模式

    单例模式(Singleton)是设计模式中很常见的一种设计模式,目的是为了让一个类在程序运行期间有且仅有一个实例,且方便全局访问。 1、私有的构造函数。 2、含有一个该类的静态私有对象。 3、静态的公有函数或属性,方便用户创建或者获取它本身的静态私有对象。 当项目

    2023年04月09日
    浏览(38)
  • 设计模式之单例模式(懒汉, 饿汉)

    单例模式是一种常用的软件设计模式, 该模式的主要目的是确保某一个类在内存中只能有一个实例对象, 通过单例模式的方法创建的类在当前进程中只有一个实例对象. 常见的单例模式有两种: 饿汉式, 这里的 “饿” 意义表述不够清晰, 用 “急” 来表述意义更加容易联想一些

    2024年02月22日
    浏览(34)
  • C#--设计模式之单例模式

    单例模式大概是所有设计模式中最简单的一种,如果在面试时被问及熟悉哪些设计模式,你可能第一个答的就是单例模式。 单例模式的实现分为两种: 饿汉式:在静态构造函数执行时就立即实例化。 懒汉式:在程序执行过程中第一次需要时再实例化。 两者有各自适用的场景

    2024年02月14日
    浏览(32)
  • 万字解析设计模式之单例模式

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个 单一 的类,该类负责创建自己的对象,同时确保 只有单个对象被创建 。这个类提供了一种访问其唯一的对象的方式,

    2024年02月08日
    浏览(28)
  • 深入理解设计模式-创建型之单例模式

    如果有些数据在系统中应该且只能保存一份,那就应该设计为单例类。 如:配置类:在系统中,我们只有一个配置文件,当配置文件被加载到内存之后,应该被映射为一个唯一的【配置实例】,此时就可以使用单例,当然也可以不用。 全局计数器:我们使用一个全局的计数

    2024年02月12日
    浏览(27)
  • 跟着GPT学设计模式之单例模式

    单例设计模式(Singleton Design Pattern)一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。 单例有几种经典的实现方式,它们分别是:饿汉式、懒汉式、双重检测、静态内部类、枚举。 处理资源访问冲突,

    2024年02月05日
    浏览(30)
  • C++设计模式创建型之单例模式

    一、概述         单例模式也称单态模式,是一种创建型模式,用于创建只能产生一个对象实例的类。例如,项目中只存在一个声音管理系统、一个配置系统、一个文件管理系统、一个日志系统等,甚至如果吧整个Windows操作系统看成一个项目,那么其中只存在一个任务管理

    2024年02月14日
    浏览(30)
  • 【Java 设计模式】创建型之单例模式

    在软件开发中,单例模式是一种常见的设计模式, 它确保一个类只有一个实例,并提供一个全局访问点 。单例模式在需要控制某些资源,如数据库连接池、线程池等共享资源的情况下非常有用。在本文中,我们将介绍 Java 设计模式中的单例模式,了解其实现方式、使用场景

    2024年01月18日
    浏览(30)
  • Gof23设计模式之单例模式(完整)

    单例模式(Singleton pattern)是Java中最简单的设计模式之一。这种设计模式属于创建型模型,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访

    2024年02月10日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包