上一篇文章中说到的 manager of managers,其中每个 manager 都是单例的实现,当然也可以使用静态类实现,但是相比于静态类的实现,单例的实现更为通用,可以适用大多数情况。

如何设计这个单例的模板?

先分析下需求,当设计一个 manager 时候,我们希望整个程序只有一个该 manager 对象实例,一般马上能想到的实现是这样的:

public class XXXManager
{
private static XXXManager instance = null; private XXXManager
{
// to do ...
} public static XXXManager()
{
if (instance == null)
{
instance = new XXXManager();
}
return instance;
}
}

如果一个游戏需要10个各种各样的manager,那么以上这些代码要复制粘贴好多遍。重复的代码太多!!!想要把重复的代码抽离出来,怎么办?答案是引入泛型。实现如下:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection; namespace QFramework
{
public abstract class QSingleton<T> where T : QSingleton<T>
{
protected static T instance = null; protected QSingleton()
{
} public static T Instance()
{
if (instance == null)
{
// 如何new 一个T???
} return instance;
}
}
}

为了可以被继承,静态实例和构造方法都使用protect修饰符。以上的问题很显而易见,那就是不能new一个泛型(3月9日补充:并不是不能new一个泛型,参考:new一个泛型的实例,编译失败了,为什么?-CSDN论坛-CSDN.NET-中国最大的IT技术社区),(4月5日补充:有同学说可以new一个泛型的实例,不过要求改泛型提供了public的构造函数,好吧,这里不用new的原因是,无法显示调用private的构造函数)。因为泛型本身不是一个类型,那该怎么办呢?答案是使用反射。实现如下:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection; /// <summary>
/// 1.泛型
/// 2.反射
/// 3.抽象类
/// 4.命名空间
/// </summary>
namespace QFramework
{
public abstract class QSingleton<T> where T : QSingleton<T>
{
protected static T instance = null; protected QSingleton()
{
} public static T Instance()
{
if (instance == null)
{
// 先获取所有非public的构造方法
ConstructorInfo[] ctors = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
// 从ctors中获取无参的构造方法
ConstructorInfo ctor = Array.Find(ctors, c => c.GetParameters().Length == 0);
if (ctor == null)
throw new Exception("Non-public ctor() not found!");
// 调用构造方法
instance = ctor.Invoke(null) as T;
} return instance;
}
}
}

以上就是最终实现了。这个实现是在任何C#程序中都是通用的。其测试用例如下所示:

using QFramework;
// 1.需要继承QSingleton。
// 2.需要实现非public的构造方法。
public class XXXManager : QSingleton<XXXManager>
{
private XXXManager()
{
// to do ...
}
} public static void main(string[] args)
{
XXXManager.Instance().xxxyyyzzz();
}

总结:

这个单例的模板是平时用得比较顺手的工具了,其实现是在其他的框架中发现的,拿来直接用了。反射的部分可能会耗一些性能,但是只会执行一次。在 Unity 中可能会需要继承 MonoBehaviour 的单例,因为很多游戏可能会只创建一个GameObject,用来获取 MonoBehaviour 的生命周期,这些内容会再下一讲中介绍:)。

转载请注明地址:凉鞋的笔记:liangxiegame.com

更多内容

Unity 游戏框架搭建 (二) 单例的模板的更多相关文章

  1. Unity 游戏框架搭建 (二十) 更安全的对象池

    上篇文章介绍了,只需通过实现IObjectFactory接口和继承Pool类,就可以很方便地实现一个SimpleObjectPool.SimpleObjectPool可以满足大部分的对象池的需求.而笔 ...

  2. Unity 游戏框架搭建 (二十三) 重构小工具 Platform

    在日常开发中,我们经常遇到或者写出这样的代码 var sTrAngeNamingVariable = "a variable"; #if UNITY_IOS || UNITY_AN ...

  3. Unity 游戏框架搭建 (二十二) 简易引用计数器

    引用计数是一个很好用的技术概念,不要被这个名字吓到了.首先来讲讲引用计数是干嘛的. 引用计数使用场景 有一间黑色的屋子,里边有一盏灯.当第一个人进屋的时候灯会打开,之后的人进来则不用再次打开了,因为已 ...

  4. Unity 游戏框架搭建 (二十一) 使用对象池时的一些细节

    上篇文章使用SafeObjectPool实现了一个简单的Msg类.代码如下: class Msg : IPoolAble,IPoolType { #region IPoolAble 实现 public ...

  5. Unity 游戏框架搭建 (十三) 无需继承的单例的模板

    之前的文章中介绍的Unity 游戏框架搭建 (二) 单例的模板和Unity 游戏框架搭建 (三) MonoBehaviour单例的模板有一些问题. 存在的问题: 只要继承了单例的模板就无法再继承其他的 ...

  6. Unity 游戏框架搭建 (十) QFramework v0.0.2小结

    从框架搭建系列的第一篇文章开始到现在有四个多月时间了,这段时间对自己来说有很多的收获,好多小伙伴和前辈不管是在评论区还是私下里给出的建议非常有参考性,在此先谢过各位. 说到是一篇小节,先列出框架的概要 ...

  7. Unity 游戏框架搭建 2018 (二) 单例的模板与最佳实践

    Unity 游戏框架搭建 2018 (二) 单例的模板与最佳实践 背景 很多开发者或者有经验的老手都会建议尽量不要用单例模式,这是有原因的. 单例模式是设计模式中最简单的也是大家通常最先接触的一种设计 ...

  8. Unity 游戏框架搭建 2018 (一) 架构、框架与 QFramework 简介

    约定 还记得上版本的第二十四篇的约定嘛?现在出来履行啦~ 为什么要重制? 之前写的专栏都是按照心情写的,在最初的时候笔者什么都不懂,而且文章的发布是按照很随性的一个顺序.结果就是说,大家都看完了,都还 ...

  9. Unity 游戏框架搭建 (十六) v0.0.1 架构调整

    背景: 前段时间用Xamarin.OSX开发一些工具,遇到了两个问题. QFramework的大部分的类耦合了Unity的API,这样导致不能在其他CLR平台使用QFramework. QFramew ...

随机推荐

  1. Java并发编程:Callable、Future和FutureTask的实现

    启动线程执行任务,如果需要在任务执行完毕之后得到任务执行结果,可以使用从Java 1.5开始提供的Callable和Future 下面就分析一下Callable.Future以及FutureTask的 ...

  2. Linux Network Management

    Linux网络管理 (YouTube视频教程) ISO/OSI七层模型 ISO: The International Organization for Standardization 国际标准化组织 ...

  3. 【charger battery 充電 充電器 電池】停充的種類

    Precondition : 配有 power path 功能的 BQ2589 手機. 接上 pc usb port. Origin : 今天有同事問我, 手機是否可以在接上 pc usb port ...

  4. nginx源码分析——线程池

    源码: nginx 1.13.0-release   一.前言      nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影 ...

  5. MyBatis通用Mapper开发

    通常情况下,MyBatis 的增删改查操作需要自己在相应xml中写相关语句, 但是运用相关工具,其实可以很方便的自动生成单表的所有增删改查(通用的多表联合查询还是需要自己写). 也可以根据具体环境,设 ...

  6. 全景智慧掌上城,飞入寻常百姓家——VR全景智慧城市

    随着腾讯和阿里陆续将AR技术加入到新年抢红包大战之中,人们对于VR.AR未来的应用空间又多了一些想象.同传统的基于二维元素的抢红包不同,借助VR.AR的技术能够让用户获得一种更加真切的体验,这种体验相 ...

  7. Predix Asset Service深度分析

    前言 在IIOT领域,面临着保存海量数据的挑战,具体到Asset层面,则要保存物理对象,逻辑对象,复杂的关系,并支持对象间的组合,分类,标签和高效查询.总结来说,可以归纳为如下几种需求:   灵活的建 ...

  8. characterEncodingFilter作用

    package com.demo.test; import java.io.IOException; import javax.servlet.Filter; import javax.servlet ...

  9. 《Python编程从入门到实践》_第五章_if语句

    条件测试 每条if语句的核心都是一个值为Ture或False的表达式,这种表达式被称为为条件测试.Python根据条件测试的值为Ture还是False来决定是否执行if语句中的代码.如果条件测试的值为 ...

  10. Ubuntu ADSL拨号上网

    一直在家用接ADSL的无线wifi上网,最近用了很久的NetGear无线路由器被我毁了,只好暂时用有线了.在Ubuntu Linux下也可直接使用ADSL拨号上网. 1.连接设备 这没什么可多说的,一 ...