概述
delegate 委托,是C#关键字,功能是函数指针,动态调用=>函数的实例的引用
Func 有返回类型的泛型委托
Action 无返回类型的泛型委托
Delegate 是一个类.委托类
Delegate的作用
当你不知道委托的类型时,可以用Delegate变量来保存,表示
class A
{
public delegate void Callback();
private Callback cb;
void Start()
{
Delegate D;
Action A1 = () => { };
Action<int, int, long> A2 = (x, y, z) => { };
Func<int> F1 = delegate () { return 666; };
D = A1;
D = A2;
D = F1;
D = cb;
}
}
Delegate 和 delegate 互转
Delegate是一个类.
delegate 是C#关键字
public delegate void Callback(); //这个我们自定义的delegate
//Delegate可以转 Action,也就是说Delegate可以转成delegate的函数声明.
Delegate d;
Action<T, U, V, X> Action = d as Action<T, U, V, X>;
Delegate =>转 delegate
public delegate void Callback();
private Callback cb;
Delegate d = cb;
cb = d as Callback;
delegate => 转 Delegate
public delegate void Callback();
private Callback cb;
Delegate d = (Callback)cb;
泛型委托 Func 与 Action 定义
Func<Result> 有返回类型的泛型委托;Result等于返回的类型,T代表arg参数的类型
Action<T> 无返回类型的泛型委托,T代表arg参数的类型
public delegate TResult Func<TResult>();
public delegate void Action();
public delegate void Callback(); //这个我们自定义的delegate.
Func<TResult> public delegate TResult Func<TResult>();
Func<T,TResult> public delegate TResult Func<T, TResult>(T arg);
Func<T1,T2,TResult>
Func<T1,T2,T3,TResult>
Func<T1,T2,T3,...T16,TResult>
Action public delegate void Action();
Action<T> public delegate void Action<T>(T obj);
Action<T1,T2> public delegate void Action<T1, T2>(T1 arg1, T2 arg2);
Action<T1,T2,T3> public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);
Action<T1,T2,T3,...T16>
匿名方法
无返回值
没有参数的写法
delegate{} //匿名方式创建的委托
()=>{} //lambda方式创建的委托, lambda表达式简化了匿名方法的书写,去掉了delegate关键字并加入 '=>' 运算符.
1个参数的写法
x=>{log(x);} //lambda方式创建的委托
delegate(string x) {log(x);} //匿名方式创建的委托
(string str) => {log(x);}; //lambda方式创建的委托 , lambda表达式简化了匿名方法的书写,去掉了
delegate关键字并加入 '=>' 运算符.
有返回值的
Func的匿名方法
Func<int> F1 = delegate () { return 666; };
Func<string, string> change = delegate(string s) { return s.ToUpper(); };
事件一般用法
public class Date : MonoBehaviour //事件改变的地方
{
Action<int> hpChange;
public int _hp = 99;
public void AddHpChangeEvent(Action<int> hpEvent) //参数为函数的变量
{
hpChange += hpEvent;
}
public int Hp
{
get{return _hp;}
set
{
_hp = value;
if (hpChange != null)
hpChange(_hp);
}
}
}
public class UIHp : MonoBehaviour //需要监听事件的地方
{
void Start()
{
Date date = GetComponent<Date>();
date.AddHpChangeEvent(hp=>{
Slider.value = hp / 100f;
});
}
}
Action用法2
public class Date //事件改变的地方
{
public Action<int> hpChange;
public int _hp = 99;
public int Hp
{
get { return _hp; }
set
{
_hp = value;
if (hpChange != null)
hpChange(_hp);
}
}
}
public class UIHp : MonoBehaviour //需要监听事件的地方
{
public void HpChange(int currt_hp)
{
Slider slider = transform.GetComponent<Slider>();
slider.value = currt_hp / 100f;
}
}
public class Player : MonoBehaviour //需要监听事件的地方
{
public void Init()
{
Date date = new Date();
var uiHp = transform.GetComponent<UIHp>();
int hp = 100;
date.hpChange += (hp2) =>
{
uiHp.HpChange(hp);
};
}
}
升级到事件中心,所有事件集中定义 ,进阶1
public class EventCenter
{
//==============================================================================================================================
//普通攻击事件,攻击方,被攻击方,伤害数值
static Action<object, object, int> AttackEvent;//普通攻击事件,攻击方,被攻击方,伤害数值
//添加监听事件 //放在OnEnable()函数里,EventCenter.AddAttackEvent(AttackEventReceive);
public static void Add_Attack_Event(Action<object, object, int> Event)
{
AttackEvent += Event;
}
//移除监听事件 //一般放在Disable()函数里
public static void Remove_Attack_Event(Action<object, object, int> Event)
{
AttackEvent -= Event;
}
//事件发起
public static void Sent_Attack_Event(object player, object enemey, int arg)
{
if (AttackEvent != null)
{
AttackEvent(player, enemey, arg);
}
}
//攻击事件接收
void Receive_Attack_Event(object player, object enemey, int arg)
{
}
//==============================================================================================================================
}
public class Enemy : MonoBehaviour
{
void OnEnable()
{
EventCenter.Add_Attack_Event(Receive_Attack_Event);
}
void OnDisable()
{
EventCenter.Remove_Attack_Event(Receive_Attack_Event);
}
//攻击事件接收
void Receive_Attack_Event(object player, object enemey, int arg)
{
}
}
委托字典 事件的封装 ,进阶2
Dictionary<EGameEvent, Delegate> dict;
Dictionary<枚举, 委托> 字典。
枚举就是我们定义的事件类型,事件可能有成百上千个都可以自己定义。
优点 通过定义一个枚举就可以添加一个事件,而不用写杂七杂八的。
缺点 匿名函数没办法取消监听,普通函数能取消。
public enum EGameEvent
{
Begin,
#region 常用事件
MainPlayer_EnterGame, // 主角进入游戏
Notice_SystemTopFinish,
Notice_SystemMidFinish,
Notice_OperTip,
#endregion
#region Packet Message
Msg_Server_Return, // 服务器返回消息
ResourceFileReady,
DBFileLoadReady,
#endregion
#region Object属性更新
Attr_Name,
Attr_ModelId,
Attr_Hp,
Attr_Exp,
Attr_GameMoney,
Attr_RealMoney,
Attr_Power,
Attr_ArmourItemID,
Attr_WeaponItemID,
Attr_Dynamic, // 所有保存在 DynamicAttrMgr 内的属性更新, 任何一个动态属性更新了, 都会发送这个消息
#endregion
#region UI公用消息
UI_ShowMainUI, // 显示主要UI
UI_HideAllUI, // 隐藏主要UI
UI_Show, // 显示界面
UI_Hide, // 关闭界面
UI_Toggle, // 显示/关闭界面
UI_ReqOriginal, // 请求原始UI资源
UI_MuliShow, //多对象UI显示
UI_MuliHide, //多对象关闭
UI_OnOriginal, // 原始UI资源准备好
UI_OnCreated, // 界面创建完毕
UI_OnShow, // UI成功显示
UI_OnHide, // UI成功隐藏
#endregion
#region 登陆界面
Login_CheckAccount, // 验证账号密码
#endregion
#region 选择服务器界面
ServerList_AddServerInfo, // 增加显示服务器信息
ServerList_SelectServer, // 选择服务器
#endregion
#region 角色创建界面
CharOper_CreatePlayer, // 界面事件 - 创建角色
CharOper_SelectClassSex, // 界面事件 - 选择职业和性别
CharOper_RandomName, // 界面事件 - 随机一个名字
#endregion
}
/*
* Advanced C# messenger by Ilya Suzdalnitski. V1.0
*
* Based on Rod Hyde's "CSharpMessenger" and Magnus Wolffelt's "CSharpMessenger Extended".
*
* Features:
* Prevents a MissingReferenceException because of a reference to a destroyed message handler.
* Option to log all messages
* Extensive error detection, preventing silent bugs
*
* Usage examples:
1. EventCenterDict.Broadcast<int>(EGameEvent.playerUpGrade,date.lv);//发起事件
EventCenterDict.AddListener<int>(EGameEvent.playerUpGrade, x =>{Debug.Log("恭喜你升到"+x+"级");});//注册监听事件
*
* Messenger cleans up its evenTable automatically upon loading of a new level.
*
* Don't forget that the messages that should survive the cleanup, should be marked with Messenger.MarkAsPermanent(string)
*
*/
using System;
using System.Collections.Generic;
using UnityEngine;
public static class EventCenterDict
{
public static bool isDebugLog = true;
static public Dictionary<EGameEvent, Delegate> mEventTable = new Dictionary<EGameEvent, Delegate>();
//Message handlers that should never be removed, regardless of calling Cleanup
static public List<EGameEvent> mPermanentMessages = new List<EGameEvent>();
//Marks a certain message as permanent.
static public void MarkAsPermanent(EGameEvent eventType)
{
Debug.Log("Messenger MarkAsPermanent t"" + eventType + """);
mPermanentMessages.Add(eventType);
}
static public void Cleanup()
{
if(isDebugLog) Debug.Log("MESSENGER Cleanup. Make sure that none of necessary listeners are removed.");
List<EGameEvent> messagesToRemove = new List<EGameEvent>();
foreach (KeyValuePair<EGameEvent, Delegate> pair in mEventTable)
{
bool wasFound = false;
foreach (EGameEvent message in mPermanentMessages)
{
if (pair.Key == message)
{
wasFound = true;
break;
}
}
if (!wasFound)
messagesToRemove.Add(pair.Key);
}
foreach (EGameEvent message in messagesToRemove)
{
mEventTable.Remove(message);
}
}
static public void PrEGameEventEventTable()
{
Debug.Log("ttt=== MESSENGER PrEGameEventEventTable ===");
foreach (KeyValuePair<EGameEvent, Delegate> pair in mEventTable)
{
Debug.Log("ttt" + pair.Key + "tt" + pair.Value);
}
Debug.Log("n");
}
static void OnListenerAdding(EGameEvent eventType, Delegate listenerBeingAdded)
{
if (isDebugLog)
Debug.Log("MESSENGER OnListenerAdding t"" + eventType + ""t{" + listenerBeingAdded.Target + " -> " + listenerBeingAdded.Method + "}");
if (!mEventTable.ContainsKey(eventType))
{
mEventTable.Add(eventType, null);
}
Delegate d = mEventTable[eventType];
if (d != null && d.GetType() != listenerBeingAdded.GetType())
{
throw new ListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType().Name, listenerBeingAdded.GetType().Name));
}
}
static void OnListenerRemoving(EGameEvent eventType, Delegate listenerBeingRemoved)
{
if (isDebugLog)
Debug.Log("MESSENGER OnListenerRemoving t"" + eventType + ""t{" + listenerBeingRemoved.Target + " -> " + listenerBeingRemoved.Method + "}");
if (mEventTable.ContainsKey(eventType))
{
Delegate d = mEventTable[eventType];
if (d == null)
{
throw new ListenerException(string.Format("Attempting to remove listener with for event type "{0}" but current listener is null.", eventType));
}
else if (d.GetType() != listenerBeingRemoved.GetType())
{
throw new ListenerException(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, d.GetType().Name, listenerBeingRemoved.GetType().Name));
}
}
else
{
throw new ListenerException(string.Format("Attempting to remove listener for type "{0}" but Messenger doesn't know about this event type.", eventType));
}
}
static public void OnListenerRemoved(EGameEvent eventType)
{
if (mEventTable[eventType] == null)
{
mEventTable.Remove(eventType);
}
}
static void OnBroadcasting(EGameEvent eventType)
{
#if REQUIRE_LISTENER
if (!mEventTable.ContainsKey(eventType)) {
}
#endif
}
static public BroadcastException CreateBroadcastSignatureException(EGameEvent eventType)
{
return new BroadcastException(string.Format("Broadcasting message "{0}" but listeners have a different signature than the broadcaster.", eventType));
}
public class BroadcastException : Exception
{
public BroadcastException(string msg)
: base(msg)
{
}
}
public class ListenerException : Exception
{
public ListenerException(string msg): base(msg)
{
}
}
//No parameters
static public void AddListener(EGameEvent eventType,Action handler)
{
OnListenerAdding(eventType, handler);
mEventTable[eventType] = (Action)mEventTable[eventType] + handler;
}
//Single parameter
static public void AddListener<T>(EGameEvent eventType, Action<T> handler)
{
OnListenerAdding(eventType, handler);
mEventTable[eventType] = (Action<T>)mEventTable[eventType] + handler;
}
//Two parameters
static public void AddListener<T, U>(EGameEvent eventType, Action<T, U> handler)
{
OnListenerAdding(eventType, handler);
mEventTable[eventType] = (Action<T, U>)mEventTable[eventType] + handler;
}
//Three parameters
static public void AddListener<T, U, V>(EGameEvent eventType, Action<T, U, V> handler)
{
OnListenerAdding(eventType, handler);
mEventTable[eventType] = (Action<T, U, V>)mEventTable[eventType] + handler;
}
//Four parameters
static public void AddListener<T, U, V, X>(EGameEvent eventType, Action<T, U, V, X> handler)
{
OnListenerAdding(eventType, handler);
mEventTable[eventType] = (Action<T, U, V, X>)mEventTable[eventType] + handler;
}
//No parameters
static public void RemoveListener(EGameEvent eventType, Action handler)
{
OnListenerRemoving(eventType, handler);
mEventTable[eventType] = (Action)mEventTable[eventType] - handler;
OnListenerRemoved(eventType);
}
//Single parameter
static public void RemoveListener<T>(EGameEvent eventType, Action<T> handler)
{
OnListenerRemoving(eventType, handler);
mEventTable[eventType] = (Action<T>)mEventTable[eventType] - handler;
OnListenerRemoved(eventType);
}
//Two parameters
static public void RemoveListener<T, U>(EGameEvent eventType, Action<T, U> handler)
{
OnListenerRemoving(eventType, handler);
mEventTable[eventType] = (Action<T, U>)mEventTable[eventType] - handler;
OnListenerRemoved(eventType);
}
//Three parameters
static public void RemoveListener<T, U, V>(EGameEvent eventType, Action<T, U, V> handler)
{
OnListenerRemoving(eventType, handler);
mEventTable[eventType] = (Action<T, U, V>)mEventTable[eventType] - handler;
OnListenerRemoved(eventType);
}
//Four parameters
static public void RemoveListener<T, U, V, X>(EGameEvent eventType, Action<T, U, V, X> handler)
{
OnListenerRemoving(eventType, handler);
mEventTable[eventType] = (Action<T, U, V, X>)mEventTable[eventType] - handler;
OnListenerRemoved(eventType);
}
//No parameters
static public void Broadcast(EGameEvent eventType)
{
if (isDebugLog)
Debug.Log("MESSENGERt" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "tttInvoking t"" + eventType + """);
OnBroadcasting(eventType);
Delegate d;
if (mEventTable.TryGetValue(eventType, out d))
{
Action Action = d as Action;
if (Action != null)
{
Action();
}
else
{
throw CreateBroadcastSignatureException(eventType);
}
}
}
//Single parameter
static public void Broadcast<T>(EGameEvent eventType, T arg1)
{
if (isDebugLog)
Debug.Log("MESSENGERt" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "tttInvoking t"" + eventType + """);
OnBroadcasting(eventType);
Delegate d;
if (mEventTable.TryGetValue(eventType, out d))
{
Action<T> Action = d as Action<T>;
if (Action != null)
{
Action(arg1);
}
else
{
throw CreateBroadcastSignatureException(eventType);
}
}
}
//Two parameters
static public void Broadcast<T, U>(EGameEvent eventType, T arg1, U arg2)
{
if (isDebugLog)
Debug.Log("MESSENGERt" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "tttInvoking t"" + eventType + """);
OnBroadcasting(eventType);
Delegate d;
if (mEventTable.TryGetValue(eventType, out d))
{
Action<T, U> Action = d as Action<T, U>;
if (Action != null)
{
Action(arg1, arg2);
}
else
{
throw CreateBroadcastSignatureException(eventType);
}
}
}
//Three parameters
static public void Broadcast<T, U, V>(EGameEvent eventType, T arg1, U arg2, V arg3)
{
if (isDebugLog)
Debug.Log("MESSENGERt" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "tttInvoking t"" + eventType + """);
OnBroadcasting(eventType);
Delegate d;
if (mEventTable.TryGetValue(eventType, out d))
{
Action<T, U, V> Action = d as Action<T, U, V>;
if (Action != null)
{
Action(arg1, arg2, arg3);
}
else
{
throw CreateBroadcastSignatureException(eventType);
}
}
}
//Four parameters
static public void Broadcast<T, U, V, X>(EGameEvent eventType, T arg1, U arg2, V arg3, X arg4)
{
if (isDebugLog)
Debug.Log("MESSENGERt" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "tttInvoking t"" + eventType + """);
OnBroadcasting(eventType);
Delegate d;
if (mEventTable.TryGetValue(eventType, out d))
{
Action<T, U, V, X> Action = d as Action<T, U, V, X>;
if (Action != null)
{
Action(arg1, arg2, arg3, arg4);
}
else
{
throw CreateBroadcastSignatureException(eventType);
}
}
}
}
事件的封装 ,进阶3
优点 可以取消对匿名函数的监听
缺点 涉及到一个装箱与拆箱的过程,频繁发送事件,效率会没有直接调用高。
/*
2019.09.05
by zack
//注册监听事件 EventSystem.RegisterEvent(事件名称, (参数))
EventSystem.RegisterEvent(111, (a, b) =>
{
Debug.Log("111");
});
EventSystem.RegisterEvent("aa", (a, b) =>
{
Debug.Log("aa");
});
EventSystem.RegisterEvent(1.2, (a, b) =>
{
Debug.Log("1.2");
});
// 发启事件
EventSystem.SendEvent(111);
EventSystem.SendEvent("aa");
EventSystem.SendEvent(1.2f);
// 取消监听111事件
EventSystem.UnRegisterEvent(111);
缺点
事件涉及到一个装箱与拆箱的过程,频繁发送事件,效率会没有直接调用高。
例子2:
// 注册事件
EventSystem.RegisterEvent("update_textbox", (a, new_text) =>
{
textBox1.AppendText(new_text[0] as string);
});
// 发启事件
EventSystem.SendEvent("update_textbox", "update_textbox1_text1");
*/
namespace Wb.EventSystem
{
using System;
using System.Collections.Generic;
using System.Reflection;
public class EventSystem : Singleton<EventSystem>
{
private readonly Dictionary<IConvertible, ListenerWrap> mAllListenerMap = new Dictionary<IConvertible, ListenerWrap>(50);
private EventSystem() { }
public void Register<T>(T key, OnEvent fun) where T : IConvertible
{
if (!mAllListenerMap.ContainsKey(key))
{
ListenerWrap wrap = new ListenerWrap();
mAllListenerMap.Add(key, wrap);
wrap.Add(fun);
return;
}
mAllListenerMap[key].Add(fun);
}
public void UnRegister<T>(T key, OnEvent fun) where T : IConvertible
{
// Debug.Log("UnRegister " + key);
if (mAllListenerMap.ContainsKey(key)) mAllListenerMap[key].Remove(fun);
}
public void UnRegister<T>(T key) where T : IConvertible
{
if (mAllListenerMap.ContainsKey(key))
{
mAllListenerMap[key].RemoveAll();
mAllListenerMap[key] = null;
mAllListenerMap.Remove(key);
}
}
public void Send<T>(T key, params object[] param) where T : IConvertible
{
if (mAllListenerMap.ContainsKey(key)) mAllListenerMap[key].Fire(key, param);
}
public void UnRegisterAll()
{
foreach (var listener in mAllListenerMap) UnRegister(listener.Key);
mAllListenerMap.Clear();
}
public static void SendEvent<T>(T key, params object[] param) where T : IConvertible
{
Instance.Send(key, param);
}
/// <summary>
/// 注册监听事件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key">事件名称</param>
/// <param name="fun">参数数组</param>
public static void RegisterEvent<T>(T key, OnEvent fun) where T : IConvertible
{
Instance.Register(key, fun);
}
public static void UnRegisterEvent<T>(T key, OnEvent fun) where T : IConvertible
{
Instance.UnRegister(key, fun);
}
public static void UnRegisterEvent<T>(T key) where T : IConvertible
{
Instance.UnRegister(key);
}
public static void UnRegisterAllEvent()
{
Instance.UnRegisterAll();
}
}
public delegate void OnEvent(IConvertible key, params object[] param);
public partial class ListenerWrap
{
private List<OnEvent> mEventList = new List<OnEvent>();
public void Fire(IConvertible key, params object[] param)
{
foreach (var func in mEventList) func(key, param);
}
public void Add(OnEvent listener)
{
if (mEventList.Contains(listener)) return;
mEventList.Add(listener);
}
public void Remove(OnEvent listener)
{
if (mEventList.Contains(listener)) mEventList.Remove(listener);
}
public void RemoveAll()
{
mEventList.Clear();
}
}
public class EventIDisposable : IDisposable
{
IConvertible key;
OnEvent fun;
public EventIDisposable(IConvertible key, OnEvent fun)
{
this.key = key;
this.fun = fun;
}
public void Dispose()
{
EventSystem.UnRegisterEvent(key, fun);
}
}
public static class SingletonCreator
{
public static T CreateSingleton<T>() where T : class, ISingleton
{
// 获取私有构造函数
var ctors = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
// 获取无参构造函数
var ctor = Array.Find(ctors, c => c.GetParameters().Length == 0);
if (ctor == null)
{
throw new Exception("Non-Public Constructor() not found! in " + typeof(T));
}
// 通过构造函数,常见实例
var retInstance = ctor.Invoke(null) as T;
retInstance.OnSingletonInit();
return retInstance;
}
}
public abstract class Singleton<T> : ISingleton where T : Singleton<T>
{
protected static T mInstance;
static object mLock = new object();
protected Singleton()
{
}
public static T Instance
{
get
{
lock (mLock)
{
if (mInstance == null)
{
mInstance = SingletonCreator.CreateSingleton<T>();
}
}
return mInstance;
}
}
public virtual void Dispose()
{
mInstance = null;
}
public virtual void OnSingletonInit()
{
}
}
public interface ISingleton
{
void OnSingletonInit();
}
}
事件中心模块
制作成就系统
任务记录
达成某种条件等等
减少代码量,减少复杂性,降低程序的耦合度
核心思路:设置事件中心将事件加进去事件发生时通知监听者
原理:基于字典和委托
KEY–事件的名字(玩家死亡,怪物死亡)
value–监听这个事件的对应的委托函数们
因为委托可以通过+=和-=所以可以有很多委托
需要事件的监听方法:两个参数,事件名和其监听者的委托函数
事件触发函数:得到哪个函数被触发了 参数是name 找到委托函数并且触发
public class EventCenter : BaseManager<EventCenter>
{
private Dictionary<string,UnityAction> eventDic =new Dictionary<string, UnityAction>();
public void AddEventListener(string name,UnityAction action)
{
if(eventDic.ContainsKey(name))
{
eventDic[name]+=action;
}
else
{
eventDic.Add(name,action);
}
}
public void EventTrigger(string name)
{
if(eventDic.ContainsKey(name))
{
eventDic[name]();
}
}
}
事件中心会由开始到程序结束都存在
将UnityAction中传入Object参数增强事件系统的通用性
public class EventCenter : BaseManager<EventCenter>
{
private Dictionary<string,UnityAction<object>> eventDic =new Dictionary<string, UnityAction<object>>();
public void AddEventListener(string name,UnityAction<object> action)
{
if(eventDic.ContainsKey(name))
{
eventDic[name]+=action;
}
else
{
eventDic.Add(name,action);
}
}
public void EventTrigger(string name,object info)
{
if(eventDic.ContainsKey(name))
{
eventDic[name](info);
}
}
public void RemoveEventListener(string name,UnityAction<object> action)
{
if(eventDic.ContainsKey(name))
eventDic[name]-=action;
}
public void Clear()
{
eventDic.Clear();
}
}
对事件中心的优化以及避免装箱拆箱
方法是使用泛型类型,字典中存储泛型的基类接口来实现减少传入参数为object带来的装箱拆箱所造成的消耗,并且使用重载的方法处理不带有类型参数的情况
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public interface IEventInfo
{
}
public class EventInfo<T> : IEventInfo
{
public UnityAction<T> actions;
public EventInfo( UnityAction<T> action)
{
actions += action;
}
}
public class EventInfo : IEventInfo
{
public UnityAction actions;
public EventInfo(UnityAction action)
{
actions += action;
}
}
/// <summary>
/// 事件中心 单例模式对象
/// 1.Dictionary
/// 2.委托
/// 3.观察者设计模式
/// 4.泛型
/// </summary>
public class EventCenter : BaseManager<EventCenter>
{
//key —— 事件的名字(比如:怪物死亡,玩家死亡,通关 等等)
//value —— 对应的是 监听这个事件 对应的委托函数们
private Dictionary<string, IEventInfo> eventDic = new Dictionary<string, IEventInfo>();
/// <summary>
/// 添加事件监听
/// </summary>
/// <param name="name">事件的名字</param>
/// <param name="action">准备用来处理事件 的委托函数</param>
public void AddEventListener<T>(string name, UnityAction<T> action)
{
//有没有对应的事件监听
//有的情况
if( eventDic.ContainsKey(name) )
{
(eventDic[name] as EventInfo<T>).actions += action;
}
//没有的情况
else
{
eventDic.Add(name, new EventInfo<T>( action ));
}
}
/// <summary>
/// 监听不需要参数传递的事件
/// </summary>
/// <param name="name"></param>
/// <param name="action"></param>
public void AddEventListener(string name, UnityAction action)
{
//有没有对应的事件监听
//有的情况
if (eventDic.ContainsKey(name))
{
(eventDic[name] as EventInfo).actions += action;
}
//没有的情况
else
{
eventDic.Add(name, new EventInfo(action));
}
}
/// <summary>
/// 移除对应的事件监听
/// </summary>
/// <param name="name">事件的名字</param>
/// <param name="action">对应之前添加的委托函数</param>
public void RemoveEventListener<T>(string name, UnityAction<T> action)
{
if (eventDic.ContainsKey(name))
(eventDic[name] as EventInfo<T>).actions -= action;
}
/// <summary>
/// 移除不需要参数的事件
/// </summary>
/// <param name="name"></param>
/// <param name="action"></param>
public void RemoveEventListener(string name, UnityAction action)
{
if (eventDic.ContainsKey(name))
(eventDic[name] as EventInfo).actions -= action;
}
/// <summary>
/// 事件触发
/// </summary>
/// <param name="name">哪一个名字的事件触发了</param>
public void EventTrigger<T>(string name, T info)
{
//有没有对应的事件监听
//有的情况
if (eventDic.ContainsKey(name))
{
//eventDic[name]();
if((eventDic[name] as EventInfo<T>).actions != null)
(eventDic[name] as EventInfo<T>).actions.Invoke(info);
//eventDic[name].Invoke(info);
}
}
/// <summary>
/// 事件触发(不需要参数的)
/// </summary>
/// <param name="name"></param>
public void EventTrigger(string name)
{
//有没有对应的事件监听
//有的情况
if (eventDic.ContainsKey(name))
{
//eventDic[name]();
if ((eventDic[name] as EventInfo).actions != null)
(eventDic[name] as EventInfo).actions.Invoke();
//eventDic[name].Invoke(info);
}
}
/// <summary>
/// 清空事件中心
/// 主要用在 场景切换时
/// </summary>
public void Clear()
{
eventDic.Clear();
}
}
注意事项及坑
1.添加监听时,回调方法并不会立即
执行
for (int i = 0; i < list.Count - 1; i++)
{
//第一段
list[i].GetComponent<Button>().onClick.AddListener(
() =>
{
OnClick(list[i + 1]);
}
);
//第二段
Button button = list[i + 1].GetComponent<Button>();
list[i].GetComponent<Button>().onClick.AddListener(
() =>
{
OnClick(button.gameObject);
}
);
}
两次碰到问题,
都是下意识的写出了第一种写法,在点击触发回调时,引用到的对象
都是数组的最后一个对象。原因是添加监听时,回调方法并不会立即
执行,所以对应的i的值也不会立即传入,而i的所有值在内存中都是
同一个地址,所以当点击触发时,i已经变成了list.count-2,所
以引用的对象都是数组的最后一个对象了。
第二种写法则是传入了对应的对象,每个对象都有自己的地址,以不
会有上述问题。
2.Task的异步回调一定要放在整个Task.Run()执行之后
private void Awake()
{
Debug.LogError("Awake");
TaskEvent(() =>
{
Debug.LogError(gameObject.GetComponent<Transform>().forward);
});
}
private async void TaskEvent(Action callBack)
{
await Task.Run(() =>
{
Debug.LogError("Task Start");
callBack.Invoke();
});
Debug.LogError("Task End");
}
很简单,使用Task异步的方式完成一些事之后,执行一个Action的回调继续干另一件事。
很常规的操作和应用场景。
但是按上述这么写完之后执行,会报下面的错误
直译就是Unity告诉我们,回调中干的事情,只能放在Unity主线程中。
我们需要修改回调中的内容么?这个思路显然是错误的,回调对于这个任务来说是抽象的,任务不应该关心,也不知道回调的内容;回调想干的事儿也不应该被Task限制。
先继续分析这个问题。
当我们把回调中的组件获取及成员变量调用删掉后,只单纯的打一个字符串LOG时,这个问题就不存在了。
这说明跨线程的Action行为本身是可以走通的,那么问题就变成了,跨线程的方法及属性调用上。
private async void TaskEvent(Action callBack)
{
await Task.Run(() =>
{
Debug.LogError("Task Start");
});
callBack.Invoke();
Debug.LogError("Task End");
}
其实最后的解决办法很简单,就是把回调从Task.Run()里挪了出来放在后面。
Task的异步在执行过程中,会把后续逻辑“停住”,待整个Task.Run()跑完之后,继续往下走。
这样出了子线程之后再调用主线程的内容,就没有问题了。
这个问题的产生主要是因为自身对Task相关内容不熟悉导致的。不过这个问题解决之后收货颇丰,总结一下:
1、Unity不支持跨线程的属性及方法调用,但是跨线程的Action本身是没有问题的;
2、Task的异步回调一定要放在整个Task.Run()执行之后,而不是放在Task.Run()的执行逻辑过程的最后。
最后
以上就是文静微笑为你收集整理的C# 委托与事件注意事项及坑的全部内容,希望文章能够帮你解决C# 委托与事件注意事项及坑所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复