每次过年在家的时候,都想着挖点坑.但是每次年后工作忙起来就在没填过.今年照例挖坑.希望年后自己能填平.
先说下事件.首先事件并不是局限的说那个关键字event.而是说这个需求实现的动作或者方向.当然这句表达的可能不太清楚.下面我会举几个例子.
我们先想这样一种场景.我们某个业务会一直运算处理一些数据,这时我们想暴露一些处理的状态给外部,比如说处理完成了一批数据或者已经没有数据可以处理的这种状态.我们当然可以把这种状态设置给某个属性上暴露出来,但是这里会出现一个问题.就是外部应该在什么时候访问这个属性.
这种场景很常见.比如你抢票的时候,订单已经提交了.当没有余票给你的时候,无论你怎么刷新页面也不会有新的状态给你.或者当你后台下载大文件的时候.不论你多久看一眼是否下载完成.都很难看一次就知道已经完成了.上面说的场景其实就是轮询.通过不断访问来确定当前的状态.那么最好的方式应该是当状态改变时有某个机制通知我完成了.比如你抢票成功了有短信提醒,下载完成时有叮的一声提示.
通过上面的例子,我们自然会想到.当业务运行到某处需要通知外部时,调用一个方法通知外部即可.这也就是说事件其实是数据传递的一种方式.他是有方向的,由内部主动传递到外部.
上面说道用一个方法通知外部,那么这个方法一定是外部的方法.在C#中当我们需要传递一个方法的时候,自然会想到Action,Func什么的,这些都是委托的一种包装.这么看来那个event关键字就很容易理解了,通过这个关键字.我们对外暴露一个特殊的属性.通过这个属性,我们可以传递或者移除一个委托给内部.内部通过调用这个事件来间接的调用那些外部方法.
当然这里面也会说到解耦啊,封装什么的说法.这里我们先不讨论这些.我们要讨论的是如何设计一个系统,让内部与外部传递数据变得更简介更方便.那言归正传,事件系统肯定是先要有事件,我们先抽象一个事件基类.先上代码.
/// <summary> /// 事件方法 /// </summary> public abstract class EventMethod { #region Field /// <summary> /// 事件句柄 /// </summary> private string handle; #endregion #region Property /// <summary> /// 事件句柄 /// </summary> public string Handle { get => handle; } #endregion #region Construction /// <summary> /// 创建一个 EventMethod 类新实例 /// </summary> public EventMethod() => handle = GetHandle(); #endregion #region Method /// <summary> /// 获取绑定的事件句柄 /// </summary> protected virtual string GetHandle() => GetType().Name; /// <summary> /// 唤醒事件 /// </summary> internal abstract void Awake(params object[] parameters); #endregion }
事件句柄是为了在事件容器内获取的事件的一个Key.这里主要的是Awake方法.当系统触发某个事件时,会在容器中通过Kye将全部的事件方法获取出来,依次调用Awake方法.事件参数不确定,所以这里Awake参数是params的.其他具体的事件通过重写Awake来进行重新重构.下面我们就重构一个按钮单击事件作为例子.
/// <summary> /// 按钮单击事件 /// </summary> public class BtnClick : EventMethod { #region Field /// <summary> /// 回调委托 /// </summary> private Action action; #endregion #region Construction /// <summary> /// 创建一个 BtnClick 类新实例 /// </summary> public BtnClick(Action action) => this.action = action; #endregion #region Method /// <summary> /// 唤醒事件 /// </summary> /// <param name="parameters"></param> internal override void Awake(params object[] parameters) => action?.Invoke(); #endregion }
以上,我们就完成了一个事件的设计.其他部分我们在后面的文章讨论