阅读学习QFramwork中的SingletonKit源码。

Singleton 普通类的单例

作为最常用的单例模块,通过继承单例泛型类来实现,需要私有构造;

 //使用第一种接口单例方式
internal class Class2Singleton : Singleton<Class2Singleton>
{
//记录被初始化的次数
private static int mIndex = 0; //私有构造
private Class2Singleton(){} public override void OnSingletonInit()
{
mIndex++;
Log(mIndex.ToString());
} public void Log(string content)
{
Debug.Log("Class2Singleton" + content);
}
}

看一下父类的代码内容,首先是ISingleton接口,方便对单例生命周期进行管理,留有给单例中数据进行初始化操作的方法。

/// <summary>
/// 单例接口
/// </summary>
public interface ISingleton
{
/// <summary>
/// 单例初始化(继承当前接口的类都需要实现该方法)
/// </summary>
void OnSingletonInit();
} /// <summary>
/// 普通类的单例
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class Singleton<T> : ISingleton where T : Singleton<T>
{
/// <summary>
/// 静态实例
/// </summary>
protected static T mInstance; /// <summary>
/// 标签锁:确保当一个线程位于代码的临界区时,另一个线程不进入临界区。
/// 如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放
/// </summary>
static object mLock = new object(); /// <summary>
/// 静态属性
/// </summary>
public static T Instance
{
get
{
lock (mLock)
{
if (mInstance == null)
{
//调用单例构造器实例化单例对象
mInstance = SingletonCreator.CreateSingleton<T>();
}
} return mInstance;
}
} /// <summary>
/// 资源释放
/// </summary>
public virtual void Dispose()
{
mInstance = null;
} /// <summary>
/// 单例初始化方法
/// </summary>
public virtual void OnSingletonInit()
{
}
}

其中调用单例构造函数中相关内容:

 internal static class SingletonCreator
{
//通过反射来进行对象构造
static T CreateNonPublicConstructorObject<T>() where T : class
{
var type = typeof(T);
// 获取私有构造函数
var constructorInfos = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic); // 获取无参构造函数
var ctor = Array.Find(constructorInfos, c => c.GetParameters().Length == 0); if (ctor == null)
{
throw new Exception("Non-Public Constructor() not found! in " + type);
} return ctor.Invoke(null) as T;
} public static T CreateSingleton<T>() where T : class, ISingleton
{
var type = typeof(T);
var monoBehaviourType = typeof(MonoBehaviour); if (monoBehaviourType.IsAssignableFrom(type))
{
return CreateMonoSingleton<T>();
}
else
{
var instance = CreateNonPublicConstructorObject<T>();
instance.OnSingletonInit();
return instance;
}
}
//······
}

编写案例调用单例;

 public class SingletonExample : MonoBehaviour
{
private void Start()
{
Class2Singleton.Instance.Log("我是第一种单例使用方式,第一次使用噢!"); Class2Singleton.Instance.Dispose(); //再次调用
Class2Singleton.Instance.Log("我是第一种单例使用方式,销毁之后,再次使用噢"); }
}

MonoSingleton继承Mono的单例

MonoSingleton是继承Mono的单例类型,继承MonoBehaviour类和ISingleton接口,其实现方式与Singleton的内容相似

 /// <summary>
/// 静态类:MonoBehaviour类的单例
/// 泛型类:Where约束表示T类型必须继承MonoSingleton<T>
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class MonoSingleton<T> : MonoBehaviour, ISingleton where T : MonoSingleton<T>
{
/// <summary>
/// 静态实例
/// </summary>
protected static T mInstance; /// <summary>
/// 静态属性:封装相关实例对象
/// </summary>
public static T Instance
{
get
{
if (mInstance == null && !mOnApplicationQuit)
{
mInstance = SingletonCreator.CreateMonoSingleton<T>();
} return mInstance;
}
} /// <summary>
/// 实现接口的单例初始化
/// </summary>
public virtual void OnSingletonInit()
{
} /// <summary>
/// 资源释放
/// </summary>
public virtual void Dispose()
{ if (SingletonCreator.IsUnitTestMode)
{
var curTrans = transform;
//清除单元测试创建的单例对象
do
{
var parent = curTrans.parent;
DestroyImmediate(curTrans.gameObject);
curTrans = parent;
} while (curTrans != null); mInstance = null;
}
else
{
Destroy(gameObject);
}
} /// <summary>
/// 当前应用程序是否结束 标签
/// </summary>
protected static bool mOnApplicationQuit = false; /// <summary>
/// 应用程序退出:释放当前对象并销毁相关GameObject
/// </summary>
protected virtual void OnApplicationQuit()
{
mOnApplicationQuit = true;
if (mInstance == null) return;
Destroy(mInstance.gameObject);
mInstance = null;
} /// <summary>
/// 释放当前对象
/// </summary>
protected virtual void OnDestroy()
{
mInstance = null;
} /// <summary>
/// 判断当前应用程序是否退出
/// </summary>
public static bool IsApplicationQuit
{
get { return mOnApplicationQuit; }
}
}

来编写案例来使用Mono单例

namespace QFramework.Example
{
//使用MonoSingletonExample
internal class Class2MonoSingleton : MonoSingleton<Class2MonoSingleton>
{
public override void OnSingletonInit()
{
Debug.Log(name + "---" + "OnSingletonInit");
} private void Awake()
{
Debug.Log(name + "---" + "awake");
} private void Start()
{
Debug.Log(name + "---" + "start");
} protected override void OnDestroy()
{
base.OnDestroy(); Debug.Log(name + "---" + "OnDestroy");
}
}
public class MonoSingletonExample : MonoBehaviour
{
private IEnumerator Start()
{
var instance = Class2MonoSingleton.Instance; yield return new WaitForSeconds(5.0f); instance.Dispose();
} }
}

5s后对单例对象进行释放:

使用MonoSingletonProperty/SingletonProperty两个工具类来封装单例,在实现设置单例类时候不需要再继承 MonoSingleton 或 Singleton ,只需要继承ISingleton接口即可,当然,此处的接口作用就是对该类进行标记说说明,表明类是单例模式。下面看下两个工具类的具体实现:

 /// <summary>
/// 属性单例类
/// </summary>
/// <typeparam name="T"></typeparam>
public static class SingletonProperty<T> where T : class, ISingleton
{
/// <summary>
/// 静态实例
/// </summary>
private static T mInstance; /// <summary>
/// 标签锁
/// </summary>
private static readonly object mLock = new object(); /// <summary>
/// 静态属性
/// </summary>
public static T Instance
{
get
{
lock (mLock)
{
if (mInstance == null)
{
mInstance = SingletonCreator.CreateSingleton<T>();
}
} return mInstance;
}
} /// <summary>
/// 资源释放
/// </summary>
public static void Dispose()
{
mInstance = null;
}
} /// <summary>
/// 继承Mono的属性单例?
/// </summary>
/// <typeparam name="T"></typeparam>
public static class MonoSingletonProperty<T> where T : MonoBehaviour, ISingleton
{
private static T mInstance; public static T Instance
{
get
{
if (null == mInstance)
{
mInstance = SingletonCreator.CreateMonoSingleton<T>();
} return mInstance;
}
} public static void Dispose()
{
if (SingletonCreator.IsUnitTestMode)
{
UnityEngine.Object.DestroyImmediate(mInstance.gameObject);
}
else
{
UnityEngine.Object.Destroy(mInstance.gameObject);
} mInstance = null;
}
}

使用SignetonProperty来实现单例类:

 internal class Class2SingletonProperty : ISingleton
{
private static int mIndex = 0; //静态单例属性
public static Class2SingletonProperty Instance
{
get { return SingletonProperty<Class2SingletonProperty>.Instance; }
}
public void OnSingletonInit()
{
mIndex++;
} private Class2SingletonProperty()
{
} public void Dispose()
{
SingletonProperty<Class2SingletonProperty>.Dispose();
} public void Log(string content)
{
Debug.Log("Class2SingletonProperty" + mIndex + "---" + content);
} }
public class SingletonPropertyExample : MonoBehaviour
{
private void Start()
{
Class2SignetonProperty.Instance.Log("Hello Tony!"); Class2SignetonProperty.Instance.Dispose(); Class2SignetonProperty.Instance.Log("Hello TonyChang");
}
}

其运行结果和第一个单例实现案例结果相同。

使用MonoSingletonProperty实现单例类:


internal class Class2MonoSingletonProperty : MonoBehaviour, ISingleton
{
//仅仅需要声明静态属性即可
public static Class2MonoSingletonProperty Instance
{
get { return MonoSingletonProperty<Class2MonoSingletonProperty>.Instance; }
} //继承ISingleton 接口实现方法
public void OnSingletonInit()
{
Debug.Log(name + "==" + "OnSingletonInit" );
} public void Dispose()
{
MonoSingletonProperty<Class2MonoSingletonProperty>.Dispose();
} private void Awake()
{
Debug.Log(name + "==" + "Awake" );
} private void Start()
{
Debug.Log(name + "==" + "Start" );
} private void OnDestroy()
{
Debug.Log(name + "==" + "OnDestroy" );
}
}
public class MonoSingletonPropertyExample:MonoBehaviour
{
private IEnumerator Start()
{
var instance = Class2MonoSingletonProperty.Instance; yield return new WaitForSeconds(5.0f); instance.Dispose();
}
}

其运行结果和第二个继承Mono的单例实现案例结果相同。

相关问题:

那么不免产生疑问--使用MonoSingletonProperty/SingletonProperty两个工具类来封装单例好处是什么?或者说解决什么问题。

我们看不使用MonoSingletonProperty实现单例,需要继承抽象类MonoSingleton,由于C#中只支持class单继承,如果我想要此单例类再继承一个Mono相关的类,那么将增加很多的编码工作。

而使用MonoSingletonProperty之后是继承接口,可以方便继承Mono相关的类。

那么使用ISingleton接口的作用是什么?

作为泛型约束,也就是说想要成为单例的类必须要继承ISingleton接口才可以使用MonoSingletonProperty/SingletonProperty两个工具类。

变相的表明,想要加入单例圈子,需要继承此接口来打个标签。

UML图

源码地址:https://github.com/liangxiegame/SingletonKit

SingletonKit单例源码阅读学习的更多相关文章

  1. vnpy源码阅读学习(1):准备工作

    vnpy源码阅读学习 目标 通过阅读vnpy,学习量化交易系统的一些设计思路和理念. 通过阅读vnpy学习python项目开发的一些技巧和范式 通过vnpy的设计,可以用python复现一个小型简单的 ...

  2. Spring源码阅读学习一

    昨天抽时间阅读Spring源码,先从spring 4.x的core包开始吧,除了core和util里,首当其冲的就是asm和cglib. 要实现两个类实例之间的字段的复制功能: 多年之前用C#,因为阅 ...

  3. vnpy源码阅读学习(5):关于MainEngine的代码阅读

    关于MainEngine的代码阅读 在入口文件中,我们看到了除了窗体界面的产生,还有关于MainEngine和EventEngin部分.今天来学习下MainEngine的代码. 首先在run代码中,我 ...

  4. vnpy源码阅读学习(8):关于app

    关于app 在入口程序中,我们看到了把 gateway,app, 各类的engine都添加到mainEngine中来.不难猜测gateway主要是处理跟外部的行情,接口各方面的代码,通过别人的文章也不 ...

  5. vnpy源码阅读学习(9)回到OptionMaster

    回到OptionMaster 根据我们对APP调用的代码阅读,我们基本上知道了一个APP是如何被调用,那么我们回到OptionMaster学习下这个APP的实现. 看看结构 class OptionM ...

  6. vnpy源码阅读学习(2):学习PyQt5

    PyQt5的学习 花费了一个下午把PyQt5大概的学习了下.找了一个教程 PyQt5教程 跟着挨着把上面的案例做了一遍,大概知道PyQt5是如何生成窗体,以及控件的.基本上做到如果有需求要实现,查查手 ...

  7. vnpy源码阅读学习(3):学习vnpy的界面的实现

    学习vnpy的界面的实现 通过简单的学习了PyQt5的一些代码以后,我们基本上可以理解PyQt的一些用法,下面让我们来先研究下vnpy的UI部分的代码. 首先回到上一节看到的run.py(/vnpy/ ...

  8. vnpy源码阅读学习(4):自己写一个类似vnpy的UI框架

    自己写一个类似vnpy的界面框架 概述 通过之前3次对vnpy的界面代码的研究,我们去模仿做一个vn.py的大框架.巩固一下PyQt5的学习. 这部分的代码相对来说没有难度和深度,基本上就是把PyQt ...

  9. requests源码阅读学习笔记

    0:此文并不想拆requests的功能,目的仅仅只是让自己以后写的代码更pythonic.可能会涉及到一部分requests的功能模块,但全看心情. 1.另一种类的初始化方式 class Reques ...

  10. pg3 bypass源码阅读 —— 学习x64内核hook跳板技术

    如之前描述的 pg3复杂了许多 先来看看都要hook哪些点 1.hook dpc和定时器分发器,防止seh路线触发pg KiTimerListExpire,KiRetireDpcList 看一下hoo ...

随机推荐

  1. 阿里云GanosBase重磅升级,发布首个云孪生时空数据库

    ​简介: GanosBase是李飞飞带领的达摩院数据库与存储实验室联合阿里云共同研发的新一代位置智能引擎:本次重磅升级为V4.0版本,推出首个云孪生时空数据库. ​ 作者 | 谢炯 来源 | 阿里技术 ...

  2. dotnet 6 已知问题 获取 CultureInfo.NumberFormat 可能抛出 IndexOutOfRangeException 异常

    本文记录一个 dotnet 6 已知问题,准确来说这是一个在 dotnet 5 引入的问题,到 dotnet 6.0.12 还没修.在获取 CultureInfo.NumberFormat 属性时,在 ...

  3. 什么是IPD项目管理模式?聊聊IPD下的产品研发流程

    IPD(集成产品开发)涵盖了产品从创意提出到研发.生产.运营等,包含了产品开发到营销运营的整个过程.围绕产品(或项目)生命周期的过程的管理模式,是一套生产流程,更是时下国际先进的管理体系.IPD(集成 ...

  4. 聊聊流言协议(Gossip)

    什么是流言协议? 在分布式系统中,以下两个是典型的问题: 维护系统状态(节点的活跃性) 节点间的通信 解决这些问题的解决方案之一如下: 集中式状态管理服务 对等状态管理服务 集中式状态管理服务 像 A ...

  5. Go类型断言demo

    Go类型断言demo package main import ( "bytes" "encoding/json" "fmt" "i ...

  6. JS实现下拉框切换和tab标签切换

    现在商城网页上会有下拉框切换内容,是如何实现的呢,研究了一天,在调整js代码和查找bug.最终完成了自己想要的效果,我没有写CSS样式,只是实现了基本功能,如果对你有所帮助,可以自己写css,使其更加 ...

  7. 怎么样给Oracle数据库中的表添加列?

    首发微信公众号:SQL数据库运维 原文链接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247485212&idx=1 ...

  8. Unity Linear Gamma色彩空间矫正测试

    Gamma和Linear修正的问题相信网上已经有很多文章了.简单来说显示器的颜色输出不是线性的,根据硬件参数和输出颜色 信息拟合曲线是x^2.2,因此会使用一个x^0.45曲线将其拟合回线性. 因为0 ...

  9. SpringMVC 项目集成 PageOffice V6 最简单代码

    本文描述了PageOffice产品在SpringMVC项目中如何集成调用. 新建SpringMVC项目:pageoffice6-springmvc-simple 在您项目的pom.xml中通过下面的代 ...

  10. java获取Linux和window系统多网卡mac地址和IP

    public static List<Map<String, String>> getMacAndIp() throws SocketException { List<M ...