Unity3D C# 基于观察者模式的全局消息机制(事件系统)

本文将从思路和实现讲解基于观察者模式的全局消息机制的实现过程
如果喜欢请给我的博客或者我的项目点个免费的star吧
项目内包含本文全部完整源码(可运行)

一、消息机制

虽然前两篇文章以及写过消息机制是为何物了,但是这里我还是想重申一下,但是稍微简略
在这里插入图片描述

  1. Unity C# 实现简易消息机制
  2. 消息机制填坑笔记(2)

消息机制用于不同的类、模块之间的通信,让模块之间相互解耦,与消息中心耦合
A与B之间不再高耦合,而是分别与中心耦合,好处就是当模块数量倍增时,单一模块崩溃不会产生过大的异常,而且方便一个模块对多个模块广播他的命令
在这里插入图片描述

二、观察者模式

在一个神秘的丛林里,有一群老鼠和一只猫,每只老鼠都在观察者猫,猫只要有风吹草动就会被几百只老鼠知道。
这就是观察者模式,我们换一个更简单的例子来说
在这里插入图片描述

我们几百个读者都订阅了人民日报公众号,人民日报更新时我们几百个人会收到同样的内容
这也是观察者模式,不过因为是用现实中的软件的实现所以更加贴合我们的程序。
我们可以根据需要选择是否订阅这个公众号,和是否取关,而公众号可以决定是否发布。
这就是观察者模式消息机制的抽象模型

订阅(Subscribe)
取消订阅(UnSubscribe)
发布(Publish)

三、实现方向的确定

泛型:需要写很多泛型的重载代码不好维护,但是用的时候语法很舒服,性能高
元组:需要用户自己元组类型转换,语法难受,性能高
params:方便维护,语法舒服,性能瓶颈严重

前面说到了元组,params,泛型等方式,我们考虑到性能问题,还是选择使用泛型避免装箱拆箱,同时能把最大的舒适度交给用户,用户不用写元组的类型转换了,苦的只是程序员本身,作为一个轮子泛型应该是很合适的了。(让chatgpt帮我生成重复的泛型,233)
前面也说到了

string : 有GC问题,方便但是也不好管理事件总和
enum:需要手动添加,或者工具生成,性能高,容易管理
long:需要写工具生成long,性能高,容易管理
interface: 实现空接口略微比较麻烦,不好管理所有事件,但是比较严谨
object:不做限制有装箱问题,不值得

之前选择的key都或多或少存在各种问题,但是我最终选择的interface,表面上看起来不合适的方法,但是其实这样是合理的,string和object显然是不能选择的,enum和long有很多项目在使用,但是我不想写这个很复杂的工具生成,会让用户手忙脚乱,相比之下接口就是最合适的选择了。
我要求能接受消息的对象必须实现IMessageReceiver,我要求所有消息类型实现IMessage,他们都是空接口,我可以根据这个模型建立一个四层结构

  • 管理器(Manager)>派发器(Dispatcher)>事件组(DelegateGroup)>事件集合

其中他们的抽象模型为

  • 管理器为 Dictionar<Type,Dispatcher> Type为消息类型,根据消息类型找到该类消息的派发器

  • 派发器为 Dictionary<IMessageReceiver,DelegateGroup> 根据接收者实例确定事件组

  • 事件表为 Dictionary<Type,List<Delegate.>> ,Type为委托类型以支持重载,根据委托类型确定具体事件集合

Framework.Message.Operator<IAwake>(this).Publish(1, 2, 100.0f);

我可以根据”消息类型“,”接收者“,”参数“来确定所有的定位条件

四、事件组

我们要实现一个存储委托类型和该类型委托列表的字典Dictionary<Type, List> events,但是以下代码不包含对象池,可能不能正常运行,仅作参考

using System;
using System.Collections.Generic;namespace AirFramework
{/// <summary>/// 委托组(方法组委托)/// </summary>public partial class DelegateGroup : Unit{private Dictionary<Type, UnitList<Delegate>> events = new();/// <summary>/// 委托类型数/// </summary>public int Count => events.Count;/// <summary>/// 委托总数(计算重载)/// </summary>public int CountAll{get{int allCount = 0;foreach (var kvp in events){allCount += kvp.Value.Value.Count;}return allCount;}}}
}
namespace AirFramework
{/// <summary>/// 委托组(方法组委托)/// </summary>public partial class DelegateGroup : Unit{internal static Func<UnitList<Delegate>> GetUnitListFromPool = () => Framework.Pool.Allocate<UnitList<Delegate>>();/// <summary>/// 移除该类型委托/// </summary>/// <param name="deleType"></param>public void Remove(Type deleType){events.TryRemoveAndDispose(deleType);}/// 返回同类型可空委托列表,禁止长期持有返回值的引用,该引用在对象池回收后无效,且可能存在元素的数量改变public List<Delegate> Get<DelegateType>(){return GetDelegateList(typeof(DelegateType));}/// <summary>/// 添加委托/// </summary>public void Add(Delegate dele, Type deleType){events.GetValueOrAddDefault(deleType, GetUnitListFromPool).Value.Add(dele);}/// <summary>/// 移除委托/// </summary>public void Remove(Delegate dele, Type deleType){if (events.TryGetValue(deleType, out var kvp)){kvp.Value.Remove(dele);if (kvp.Value.Count == 0){events.RemoveAndDispose(deleType);}}}/// <summary>/// 返回同类型可空委托列表,禁止长期持有返回值的引用,该引用在对象池回收后无效,且可能存在元素的数量改变/// </summary>/// <param name="deleType"></param>/// <returns></returns>public List<Delegate> GetDelegateList(Type deleType){return events.GetValueOrDefault(deleType)?.Value;} }
}

五、派发器

派发器负责 具体对象到事件组的获取

/********************************************************************************************* Author : yueh0607* Date : 2023.1.30* Description : * 抽象模型消息派发器,负责处理每类消息的派发任务,可以指定接收者与派发类型*/using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;namespace AirFramework
{/// <summary>/// 消息分发器/// </summary>public partial class MessageDispatcher : Unit{public int Count => events.Count;private Dictionary<IMessageReceiver, UnitDelegateGroup> events = new();internal Dictionary<IMessageReceiver,UnitDelegateGroup> EevensList=> events;}/// <summary>/// 消息分发器/// </summary>public partial class MessageDispatcher : Unit{//从对象池获取internal static Func<UnitDelegateGroup> GetGroupFromPool = () => Framework.Pool.Allocate<UnitDelegateGroup>();/// <summary>/// 访问或添加:获取指定接收者的委托派发组/// </summary>/// <param name="receiver"></param>/// <returns></returns>public UnitDelegateGroup GetOrAddGroup(IMessageReceiver receiver){return events.GetValueOrAddDefault(receiver, GetGroupFromPool);}/// <summary>/// 添加:为接收者添加指定的委托派发/// </summary>public void Add(IMessageReceiver receiver, Type deleType, Delegate dele){events.GetValueOrAddDefault(receiver, GetGroupFromPool).Value.Add(dele, deleType);}/// <summary>/// 移除:为接收者移除指定委托派发/// </summary>public void Remove(IMessageReceiver receiver, Type deleType, Delegate dele){if (events.TryGetValue(receiver, out var group)){group.Value.Remove(dele, deleType);if (group.Value.Count == 0){events.RemoveAndDispose(receiver);}}}/// <summary>/// 移除:为接收者移除全部委托派发/// </summary>public void Remove(IMessageReceiver receiver){events.TryRemoveAndDispose(receiver);}}
}

六、管理器


using System;
using System.Collections.Generic;
using System.Linq;namespace AirFramework
{/// <summary>/// 消息派发管理器/// </summary>public partial class MessageManager : GlobalManager, IMessageReceiver{/// <summary>/// 消息派发器存储器/// </summary>private Dictionary<Type, UnitMessageDispatcher> dispatchers = new();/// <summary>/// 消息移除:移除全局所有的该类消息/// </summary>public void Remove<T>() where T : IMessage{UnRegister(typeof(T));}/// <summary>/// 消息移除:移除该对象所有的该类消息/// </summary>public void Remove<T>(IMessageReceiver receiver) where T : IMessage{UnRegister(typeof(T), receiver);}}
}

using System;
using System.Collections.Generic;
using System.Linq;namespace AirFramework
{/// <summary>/// 消息派发管理器/// </summary>public partial class MessageManager : GlobalManager, IMessageReceiver{internal static Func<UnitMessageDispatcher> GetDispatcherFromPool = () => Framework.Pool.Allocate<UnitMessageDispatcher>();/// <summary>/// 基础消息注册/// </summary>internal void Register(Type messageType, IMessageReceiver receiver, Type deleType, Delegate message){dispatchers.GetValueOrAddDefault(messageType,GetDispatcherFromPool).Value.Add(receiver, deleType, message);}/// <summary>/// 基础消息移除/// </summary>internal void UnRegister(Type messageType, IMessageReceiver receiver, Type deleType, Delegate message){if(dispatchers.TryGetValue(messageType,out var dispatcher)){dispatcher.Value.Remove(receiver, deleType, message);if (dispatcher.Value.Count == 0){dispatchers.RemoveAndDispose(messageType);}}}/// <summary>/// 基础消息移除:移除接收者的全部该类型消息/// </summary>internal void UnRegister(Type messageType, IMessageReceiver receiver){if (dispatchers.TryGetValue(messageType, out var dispatcher)){dispatcher.Value.Remove(receiver);if (dispatcher.Value.Count == 0){dispatchers.RemoveAndDispose(messageType);}}}/// <summary>/// 基础消息移除:移除全部该类型消息/// </summary>/// <param name="messageType"></param>internal void UnRegister(Type messageType){dispatchers.TryRemoveAndDispose(messageType);}/// <summary>/// 基础消息移除:移除全局所有消息/// </summary>internal void UnRegister(){dispatchers.ClearAndDispose();}/// <summary>/// 基础消息移除:移除对象身上全部消息/// </summary>/// <param name="receiver"></param>internal void UnRegister(IMessageReceiver receiver){var queue = Framework.Pool.Allocate<UnitQueue<Type>>();foreach(var dispatcher in dispatchers){dispatcher.Value.Value.Remove(receiver);if (dispatcher.Value.Value.Count == 0){queue.Value.Enqueue(dispatcher.Key);}}while(queue.Value.Count > 0) dispatchers.Remove(queue.Value.Dequeue());queue.Dispose();} }
}

七、拓展方法

我们实现了基本的注册和取消注册,还有获取,但没有发布,通过添加拓展的方式,我们就实现了上文提到的语法

		 public static UnitDelegateGroup Operator<MessageType>(this IMessageReceiver receiver) where MessageType : IMessage{return Framework.Message.Operator<MessageType>(receiver);}/// 发布public static void Publish(this UnitDelegateGroup container){var events = container?.Value.Get<Action>();if (events == null ||events.Count==0) return;for(int i=0; i<events.Count; i++){(events[i] as Action)?.Invoke();}}/// <summary>/// 发布/// </summary>public static void Publish<T1>(this UnitDelegateGroup container, T1 arg1){var events = container?.Value.Get<Action<T1>>();if (events == null || events.Count == 0) return;for (int i = 0; i < events.Count; i++){(events[i] as Action<T1>)?.Invoke(arg1);}}public static void Subscribe(this UnitDelegateGroup container,Action message)=>container?.Value.Add<Action>(message);
public static void UnSubscribe(this UnitDelegateGroup container, Action message)=> container?.Value.Remove<Action>(message);

八、 带返回值的问答

我们有时需要消息带有返回值,在上文基础上我可以这样调用

public static void Response<T1>(this UnitDelegateGroup container, Func<T1> message)=>container?.Value.Add<Func<T1>>(message);
public static void Cancel<T1>(this UnitDelegateGroup container, Func<T1> message)=> container?.Value.Remove<Func<T1>>(message);public static bool TryCall<T1>(this UnitDelegateGroup container,out T1 result){var events = container?.Value.Get<Func<T1>>();if (events == null || events.Count == 0){result = default;return false;}result= (events[0] as Func<T1>).Invoke();return true;}

九、使用方式

Framework.Message.Operator<IAwake>().Subscribe<int,int,float>(myMethod);//注册管理器消息
Framework.Message.Operator<IAwake>().Publish(1, 2, 100.0f);//管理器消息
Framework.Message.Dispatcher<IAwake>().Publish(1,2,100.0f);//全局消息包含包含管理器

我们甚至可以加一下拓展,实现this.Operator的简化语法
本文就到这里,至此消息机制系列结束,作者现在大一填完高一时的坑了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/7608.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity之ChatGPT提问

Unity中normalized的含义及用途 今天偶然发现一个不用账号也可以使用ChatGPT的网站&#xff0c;就问了几个关于normalized的问题&#xff0c;也顺便让自己深刻理解向量归一化用途&#xff0c;感觉真的是太强大了&#xff0c;以下是我提的问题。 问题1.Unity normalized 的意思…

ChatGPT 提问,软件杂项部分

堆内存与栈内存一般分别 有多少 ChatGPT 堆内存和栈内存的大小取决于操作系统和编译器的限制以及程序的运行环境。以下是一些常见的默认大小范围&#xff0c;但请注意这些值可以因环境而异&#xff1a; 栈内存大小&#xff1a; Windows平台&#xff1a;默认情况下&#xff…

目标是复制 ChatGPT?又一开源替代品来了

翻译 | 王子彧 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 随着 ChatGPT 火爆出圈后&#xff0c;国内外企业也相继推出类 ChatGPT 产品&#xff0c;如百度文心一言、阿里通义千问&#xff0c;微软新 Bing、Google Bard&#xff0c;Adobe Firefly 等等。 最近…

加速与 ChatGPT 交互,用 ChatClipboard 轻松复制粘贴 AI 响应!

ChatClipboard 中文简介 ChatClipboard 是一个方便的桌面应用程序&#xff0c;旨在通过简单的几个步骤&#xff0c;让用户能够快速获取 ChatGPT 的响应结果。 当你需要寻求 ChatGPT 的帮助时&#xff0c;只需将文本复制到剪贴板中&#xff0c;然后单击 ChatClipboard 中的按…

GPT-4 Technical Report译文

我们创建了 GPT-4&#xff0c;这是 OpenAI 努力扩展深度学习的最新里程碑。GPT-4 是一个大型多模态模型&#xff08;接受图像和文本输入&#xff0c;发出文本输出&#xff09;&#xff0c;虽然在许多现实世界场景中的能力不如人类&#xff0c;但在各种专业和学术基准上表现出人…

无需注册,不限次数!北大团队搞出ChatExcel

来源&#xff1a;量子位 做Excel表&#xff0c;真就动动嘴就够了&#xff01; 看&#xff0c;输入想要干的事&#xff1a;给学生成绩排个名吧。 简单敲个回车&#xff0c;表格唰一下就列好了&#xff01; 检查一遍也没错。 还能跨表格处理。 比如标记出两张不同表格中排名都在前…

让ChatGPT告诉你Java的发展前景

Java版电商购物系统项目实战 最近很多人问我Java的发展前景怎么样&#xff1f;该怎么学Java基础&#xff1f;java这么卷还该不该学等等。那今天老王以电商场景为例&#xff0c;再结合ChatGPT的回答和大家聊的一下Java有哪些应用前景和技术层面的落地方案。&#xff08;在收获干…

easyExcel导入表格

easyExcel导入表格 本文章是介绍java&#xff0c;通过easyExcel导入较为复杂的表格 导入表格如下 前提工作导包 <!-- hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.6.0<…

【根据模板导出多sheet表格数据】

文章目录 1. 根据模板导出多sheet表格数据1. 先上代码2. 模板展示3. 数据构造1.实体类创建2. sql数据构造(主要是需要把结果集别名映射到对应的实体类上) 2. 无模板多sheet导出1. code部分 3. 效果演示image-20220930172145001结言 本文章主要是记录自己的一些心得和之前写过的…

如何将excel表格数据导入postgresql数据库

作者&#xff1a;瀚高PG实验室 &#xff08;Highgo PG Lab&#xff09;- 禹晓 实际的工作中&#xff0c;我们经常会碰到统计数据的工作&#xff0c;有些维度的统计数据因为工作需要我们需要导出为excel作为报表附件供不同的部门审查。为了方便以后的对比工作&#xff0c;领导会…

table表格直接导出Excel文件

注&#xff1a;如遇到复杂表格时可用此方法做前端导出 导出事件 handleDown() {var html "<html><head><meta charsetutf-8 /></head><body>" document.getElementsByTagName("table")[0].outerHTML "</body&g…

将ChatGPT整合到Word中

引言 自ChatGPT出现&#xff0c;各种基于它的软件扩展纷至沓来&#xff0c;目前Word支持ChatGPT的add-in有两款&#xff0c;可以通过: 插入->获取加载项->搜索openai查看。 其中Ghostwriter从介绍上看功能比较单一&#xff0c;而且软件需要购买&#xff0c;用自己的API-k…

用上ChatGPT的这几个功能,你的开发效率不高都难

ChatGPT去年12月份开始火得不行了&#xff0c;它彻底改变了程序员开发代码的方式。怎么来提高程序员开发程序的效率呢&#xff1f;可能现在大多数软件开发人员仍然没有习惯使用 ChatGPT&#xff0c;本文将介绍几种方式来提升和简化自己的工作。 一、重构你的代码 如果你是一个开…

Intellij:自然语言到代码自动生成 by ChatGPT

背景 Intellij是一个非常流行的开发工具&#xff0c;它被广泛用于软件开发。随着OpenAI训练的ChatGPT模型越来越智能&#xff0c;我们开发了一个名为EDQL的Intellij插件。这个插件可以将ChatGPT的智能写代码能力转化为代码。 安装和尝试: https://github.com/chengpohi/edql/re…

chatgpt到底颠覆了什么 第二部分

以第二个理由就是两个字&#xff0c;垄断。 现在谈到范式转变&#xff0c;如果首先谈的还是算法&#xff0c;那说明还没有透彻理解范式改变范式改变&#xff0c;首先要改的是什么。是什么&#xff1f;是参赛资格。 过去我相信大企业大团队聚拢了许多聪明的脑袋&#xff0c;但我…

chatgpt 到底颠覆了什么 第一部分

ChatGPT一出来&#xff0c;一堆搞NLP的立马哭了。为什么&#xff1f;不该问为什么哭&#xff0c;而该问为什么还不哭。 有两个立马大哭的理由。 第一个理由很多人说了&#xff0c;范式改变。 虽然说没有哪个研究领域&#xff0c;甚至没有哪个领域敢说自己真的是天道酬勤绝对公平…

文心一言---中国版的“ChatGPT”狂飙的机会或许要出现了

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的在读研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三…

果然,ChatGPT 技术还是被拿去搞黄色了!

转自&#xff1a;顶级程序员 此前&#xff0c;推出的各类AI绘画工具。 想要制作出还能看得过去的作品&#xff0c;需要一定的美术功底和美感&#xff0c;上手门槛一点也不低。 大部分绅士顶多做个饱饱眼福的观众&#xff0c;完全没有参与感。 啪得一下很快啊&#xff0c;最近一…

ChatGPT 漩涡 :The ChatGPT Maelstrom

ChatGPT 漩涡 by Jean-Louis Gasse ChatGPT 以自 2007 年 iPhone 问世以来从未见过的方式迅速俘获了人们的想象力。今天,我们试探性地随机漫步其迅速扩张的景观。 在结束了法国和奥地利部分地区的旅行后,当我愉快地回到我的写作站时,我在我钟爱的科技世界中看到了太多诱人…

ChatGPT神奇应用:快速撰写大众点评好评

正文共 464 字&#xff0c;阅读大约需要 2 分钟 消费者必备技巧&#xff0c;您将在2分钟后获得以下超能力&#xff1a; 快速自动撰写大众点评评论 Beezy评级 &#xff1a;B级 *经过简单的寻找&#xff0c; 大部分人能立刻掌握。主要节省时间。 推荐人 | Kim 编辑者 | Linda ●…