当前位置: 首页 > 编程笔记 >

举例讲解C#编程中对设计模式中的单例模式的运用

潘凯
2023-03-14
本文向大家介绍举例讲解C#编程中对设计模式中的单例模式的运用,包括了举例讲解C#编程中对设计模式中的单例模式的运用的使用技巧和注意事项,需要的朋友参考一下

单例模式的介绍
说到单例模式,大家第一反应应该就是——什么是单例模式?,从“单例”字面意思上理解为——一个类只有一个实例,所以单例模式也就是保证一个类只有一个实例的一种实现方法罢了,下面给出单例模式的一个官方定义:确保一个类只有一个实例,并提供一个全局访问点。为了帮助大家更好地理解单例模式,大家可以结合下面的类图来进行理解,以及后面也会剖析单例模式的实现思路:

为什么会有单例模式
看完单例模式的介绍,自然大家都会有这样一个疑问——为什么要有单例模式的?它在什么情况下使用的?从单例模式的定义中我们可以看出——单例模式的使用自然是当我们的系统中某个对象只需要一个实例的情况,例如:操作系统中只能有一个任务管理器,操作文件时,同一时间内只允许一个实例对其操作等,既然现实生活中有这样的应用场景,自然在软件设计领域必须有这样的解决方案了(因为软件设计也是现实生活中的抽象),所以也就有了单例模式了。

剖析单例模式的实现思路
了解完了一些关于单例模式的基本概念之后,下面就为大家剖析单例模式的实现思路的,因为在我自己学习单例模式的时候,咋一看单例模式的实现代码确实很简单,也很容易看懂,但是我还是觉得它很陌生(这个可能是看的少的,或者自己在写代码中也用的少的缘故),而且心里总会这样一个疑问——为什么前人会这样去实现单例模式的呢?他们是如何思考的呢?后面经过自己的琢磨也就慢慢理清楚单例模式的实现思路了,并且此时也不再觉得单例模式模式的,下面就分享我的一个剖析过程的:

我们从单例模式的概念(确保一个类只有一个实例,并提供一个访问它的全局访问点)入手,可以把概念进行拆分为两部分:(1)确保一个类只有一个实例;(2)提供一个访问它的全局访问点;下面通过采用两人对话的方式来帮助大家更快掌握分析思路:

菜鸟:怎样确保一个类只有一个实例了?

老鸟:那就让我帮你分析下,你创建类的实例会想到用什么方式来创建的呢?

新手:用new关键字啊,只要new下就创建了该类的一个实例了,之后就可以使用该类的一些属性和实例方法了

老鸟:那你想过为什么可以使用new关键字来创建类的实例吗?

菜鸟:这个还有条件的吗?........., 哦,我想起来了,如果类定义私有的构造函数就不能在外界通过new创建实例了(注:有些初学者就会问,有时候我并没有在类中定义构造函数为什么也可以使用new来创建对象,那是因为编译器在背后做了手脚了,当编译器看到我们类中没有定义构造函数,此时编译器会帮我们生成一个公有的无参构造函数)

老鸟:不错,回答的很对,这样你的疑惑就得到解答了啊

菜鸟:那我要在哪里创建类的实例了?

老鸟:你傻啊,当然是在类里面创建了(注:这样定义私有构造函数就是上面的一个思考过程的,要创建实例,自然就要有一个变量来保存该实例把,所以就有了私有变量的声明,但是实现中是定义静态私有变量,朋友们有没有想过——这里为什么定义为静态的呢?对于这个疑问的解释为:每个线程都有自己的线程栈,定义为静态主要是为了在多线程确保类有一个实例)

菜鸟:哦,现在完全明白了,但是我还有另一个疑问——现在类实例创建在类内部,那外界如何获得该的一个实例来使用它了?

老鸟:这个,你可以定义一个公有方法或者属性来把该类的实例公开出去了(注:这样就有了公有方法的定义了,该方法就是提供方法问类的全局访问点)

通过上面的分析,相信大家也就很容易写出单例模式的实现代码了,下面就看看具体的实现代码(看完之后你会惊讶道:真是这样的!):

下面是Singleton.cs的内容:

using System; 
using System.Collections; 
using System.Collections.Generic; 
  
  
public class Singleton : MonoBehaviour 
{ 
  private static GameObject m_Container = null; 
  private static string m_Name = "Singleton"; 
  private static Dictionary<string, object> m_SingletonMap = new Dictionary<string, object>(); 
  private static bool m_IsDestroying = false; 
    
  public static bool IsDestroying 
  { 
    get { return m_IsDestroying; } 
  } 
    
  public static bool IsCreatedInstance(string Name) 
  { 
    if(m_Container == null) 
    { 
      return false; 
    } 
    if (m_SingletonMap!=null && m_SingletonMap.ContainsKey(Name))  
    { 
      return true; 
    } 
    return false; 
      
  } 
  public static object getInstance (string Name) 
  { 
    if(m_Container == null) 
    { 
      Debug.Log("Create Singleton."); 
      m_Container = new GameObject (); 
      m_Container.name = m_Name;   
      m_Container.AddComponent (typeof(Singleton)); 
    } 
    if (!m_SingletonMap.ContainsKey(Name)) { 
      if(System.Type.GetType(Name) != null) 
      { 
        m_SingletonMap.Add(Name, m_Container.AddComponent (System.Type.GetType(Name))); 
      } 
      else 
      { 
        Debug.LogWarning("Singleton Type ERROR! (" + Name + ")"); 
      } 
    } 
    return m_SingletonMap[Name]; 
  }   
    
  public void RemoveInstance(string Name) 
  { 
    if (m_Container != null && m_SingletonMap.ContainsKey(Name)) 
    { 
      UnityEngine.Object.Destroy((UnityEngine.Object)(m_SingletonMap[Name])); 
      m_SingletonMap.Remove(Name); 
        
      Debug.LogWarning("Singleton REMOVE! (" + Name + ")"); 
    } 
  } 
  
  void Awake () 
  { 
    Debug.Log("Awake Singleton."); 
    DontDestroyOnLoad (gameObject); 
  } 
    
  void Start() 
  { 
    Debug.Log("Start Singleton."); 
  }   
    
  void Update() 
  { 
  } 
    
  void OnApplicationQuit() 
  { 
    Debug.Log("Destroy Singleton"); 
    if(m_Container != null) 
    { 
      GameObject.Destroy(m_Container); 
      m_Container = null; 
      m_IsDestroying = true; 
    }       
  } 
    
}

代码大部分都比较容易看懂,下面介绍几点注意的地方:
当我们在其他代码里需要访问某个单例时,只需调用getInstance函数即可,参数是需要访问的脚本的名字。我们来看一下这个函数。它首先判断所有单例所在的容器m_Container是否为空(实际上就是场景中是否存在一个Gameobject,上面捆绑了一个Singleton脚本),如果为空,它将自动创建一个对象,然后以“Singleton”命名,再捆绑Singleton脚本。m_SingletonMap是负责维护所有单例的映射。当第一次访问某个单例时,它会自动向m_Container上添加一个该单例类型的Component,并保存在单例映射中,再返回这个单例。因此,我们可以看出,单例的创建完全都是自动的,你完全不需要考虑在哪里、在什么时候捆绑脚本,这是多么令人高兴得事情!
在Awake函数中,有一句代码DontDestroyOnLoad (gameObject);,这是非常重要的,这句话意味着,当我们的场景发生变化时,单例模式将不受任何影响。除此之外,我们还要注意到,这句话也必须放到Awake函数,而不能放到Start函数中,这是由两个函数的执行顺序决定的,如果反过来,便可能会造成访问单例不成功,下面的例子里会更详细的介绍;
在OnApplicationQuit函数中,我们将销毁单例模式。
最后一点很重要:一定不要在OnDestroy函数中直接访问单例模式!这样很有可能会造成单例无法销毁。这是因为,当程序退出准备销毁单例模式时,我们在其他脚本的OnDestroy函数中再次请求访问它,这样将重新构造一个新的单例而不会被销毁(因为之前已经销毁过一次了)。如果一定要访问的话,一定要先调用IsCreatedInstance,判断该单例是否存在。

.NET实现单例模式的类
理解完了单例模式之后,菜鸟又接着问了:.NET FrameWork类库中有没有单例模式的实现呢?

经过查看,.NET类库中确实存在单例模式的实现类,不过该类不是公开的,下面就具体看看该类的一个实现的(该类具体存在于System.dll程序集,命名空间为System,大家可以用反射工具Reflector去查看源码的):

// 该类不是一个公开类
  // 但是该类的实现应用了单例模式
  internal sealed class SR
  {
    private static SR loader;
    internal SR()
    {
    }
    // 主要是因为该类不是公有,所以这个全部访问点也定义为私有的了
    // 但是思想还是用到了单例模式的思想的
    private static SR GetLoader()
    {
      if (loader == null)
      {
        SR sr = new SR();
        Interlocked.CompareExchange<SR>(ref loader, sr, null);
      }
      return loader;
    }
    // 这个公有方法中调用了GetLoader方法的
    public static object GetObject(string name)
    {
      SR loader = GetLoader();
      if (loader == null)
      {
        return null;
      }
      return loader.resources.GetObject(name, Culture);
    }
  }

总结
到这里,设计模式的单例模式就介绍完了,希望通过本文章大家可以对单例模式有一个更深的理解,并且希望之前没接触过单例模式或觉得单例模式陌生的朋友看完之后会惊叹:原来如此!

 类似资料:
  • 本文向大家介绍详解Ruby设计模式编程中对单例模式的运用,包括了详解Ruby设计模式编程中对单例模式的运用的使用技巧和注意事项,需要的朋友参考一下 简介       单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一实例。要实现这一点,可以从客户端对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,“阻止”所有想要生成对象的访问。使用工厂方法来限

  • 本文向大家介绍C#设计模式之单例模式实例讲解,包括了C#设计模式之单例模式实例讲解的使用技巧和注意事项,需要的朋友参考一下 前言 最近开始花点心思研究下设计模式,主要还是让自己写的代码可重用性高、保证代码可靠性。所谓设计模式,我找了下定义:是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。毫无疑问,设计模式于己于他人于系统都是多赢的;设计模式使代码编制真正工程化;设计模式是软件工

  • 本文向大家介绍举例解析设计模式中的工厂方法模式在C++编程中的运用,包括了举例解析设计模式中的工厂方法模式在C++编程中的运用的使用技巧和注意事项,需要的朋友参考一下 工厂方法模式不同于简单工厂模式的地方在于工厂方法模式把对象的创建过程放到里子类里。这样工厂父对象和产品父对象一样,可以是抽象类或者接口,只定义相应的规范或操作,不涉及具体的创建或实现细节。 其类图如下: 实例代码为: 关键的实现:

  • 本文向大家介绍简单讲解在Java编程中实现设计模式中的单例模式结构,包括了简单讲解在Java编程中实现设计模式中的单例模式结构的使用技巧和注意事项,需要的朋友参考一下 1. 模式介绍 模式的定义 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 模式的使用场景 确保某个类有且只有一个对象的场景,例如创建一个对象需要消耗的资源过多,如要访问 IO 和数据库等资源。 2. UML类图

  • 本文向大家介绍JavaScript编程的单例设计模讲解,包括了JavaScript编程的单例设计模讲解的使用技巧和注意事项,需要的朋友参考一下 在Javascript中,单例模式是一种最基本又经常用到的设计模式,可能在不经意间就用到了单例模式。 本文将从最基础的理论开始,讲述单例模式的基本概念和实现,最后用一个例子来讲述单例模式的应用。 理论基础 概念 单例模式,顾名思义就是只有一个实例存在。通过

  • 本文向大家介绍解析C#设计模式之单例模式,包括了解析C#设计模式之单例模式的使用技巧和注意事项,需要的朋友参考一下   单例模式(Singleton),故名思议就是说在整个应用程序中,某一对象的实例只应该存在一个。比如,一个类加载数据库中的数据到内存中以提供只读数据,这就很适合使用单例模式,因为没有必要在内存中加载多份相同的数据,另外,有些情况下不允许内存中存在多分份相同的数据,比如数据过大,内存