有没有帮忙做网站的,建设银行如何招聘网站,打开秒开小游戏,用enfold做的网站前言
在之前我学习委托的时候#xff0c;写到了 学习了委托#xff0c;事件其实也就学习了#xff0c;事件和委托基本上一模一样#xff1a; 然而在实际工作中通过对事件的深入学习后发现#xff0c;实际上事件的使用比委托要严格一些#xff0c;本节将详细讲解事件的使… 前言
在之前我学习委托的时候写到了 学习了委托事件其实也就学习了事件和委托基本上一模一样 然而在实际工作中通过对事件的深入学习后发现实际上事件的使用比委托要严格一些本节将详细讲解事件的使用。
视频参考【事件•语法篇】如何声明自定义的事件以及事件的完整/简略声明格式 文章目录 事件的定义事件和事件模型使用事件的好处 事件的声明格式事件的完整声明小结 事件的简略声明 泛型委托定义下的事件为什么使用事件 事件的定义
事件event有能力使一个类或者对象在发生相关事情的时候去通知其他类对象们。简单来说一个事件在发生后会去通知所有的监听事件的成员函数让它们进行对应的事件处理。
乍一看事件和多播委托很像实际上事件也是委托的一种特殊的封装。
事件和事件模型 事件模型拥有五大要素分别是
事件的拥有者事件事件的响应者事件处理器事件定义(
五大要素也很好理解事件的拥有者就是定义事件的类或者对象事件的响应者就是事件多播时注册处理器Handler方法的那些类或者对象。事件就是指这个特殊的委托封装事件的处理器就是一种在委托约束下的方法。事件定义就是注册方法的操作符只能是 和 -。
事件区别于委托有一个重要的限制就是事件Event和事件处理器EventHandler必须属于同一委托类型如果不是同一委托类型则事件处理器和事件就是不匹配的。
本质上事件是基于委托的一方面事件的注册需要使用委托类型进行约束它约束了该事件应该处理什么类型的事件数据EventArgs以保证类型兼容。另一方面事件中注册的各种Handler的调度也是基于多播委托的。
使用事件的好处
使用事件的好处在于通过对委托的封装增加了一些更严格的使用规则例如事件只能放在和-的左侧就避免了对委托直接用赋值导致整个委托被重置的问题。例如事件必须定义sender和FooEventArgs就方便我们对拥有者以及传递的数据进行适当的处理。 事件的声明格式
.Net中规定声明事件的委托必须使用EventHandler作为结尾提高代码可读性。而实际上这个EventHandler也是官方给出的一种标准的委托类型
public delegate void EventHandler(object sender, EventArgs e);其中响应者或者处理者是sender类型是万物之父object也就是可接收所有类。数据类型是EventArgs这是事件的“处理数据”的基类任何事件中用于传递或处理的数据都必须继承于EventArgs这个基类。同样的继承于EventArgs类型的处理数据也需要以XXXEventArgs来命名表示它是XXXEventHandler的事件数据。
使用事件的方法是仿照上述委托类型声明一个全新的事件委托当然也可以直接使用EventHandler这个事件但是要避免由于object的类型转换所产生的装箱拆箱在直接使用EventHandler的时候如果传入不同类型的sender为了避免强转使用导致的装箱拆箱通常用as来进行隐式转换。
事件的完整声明
让我们来写一段完整的自定义事件声明的格式代码以视频中的代码为例这个事件的拥有者是客户事件是一个点单的事件事件的响应者是服务员事件处理器是客户的点单事件EventHandler
// .Net中规定声明事件的委托必须使用EventHandler作为结尾提高代码可读性
// 该委托指定了事件的类型约束其中响应者Sender的类型是Customer处理数据是OrderEventArgs
public delegate void OrderEventHandler(Customer _customer,OrderEventArgs _e);public class Customer
{public float Bill {get;set;}public void PayTheBill(){Debug.Log(I have to pay: this.Bill);}// 定义完整的事件声明格式// 这个orderEventHandler私有委托被封装在public的事件当中用于限制对委托的访问private OrderEventHandler orderEventHandler;// 定义事件OnOrder完整声明类似于属性需要定义基本的添加器和移除器public event OrderEventHandler OnOrder{add{orderEventHandler value;}remove{orderEventHandler - value;}}
}// 继承了EventArgs基类的对应事件的处理数据并定义其内部属性
public class OrderEventArgs : EventArgs
{public string CoffeeName {get;set;}public string CoffeeSize {get;set;}public float CoffeePrice {get;set;}
}现在我们已经准备好了一个事件和它的拥有者接下来需要一个响应者来处理事件。
public class EventEx : MonoBehavior
{Customer customer new Customer();Waiter waiter new Waiter();private void Start(){customer.OnOrder waiter.TakeAction;}
}public class Waiter
{事件响应通过事件传递的事件数据中的咖啡size的类型来判断每个客户的订单应该收什么价格。internal void TakeAction(Customer _customer, OrderEventArgs _e){float finalPrice 0;switch(_e.CoffeeSize){case Tall:finalPrice _e.CoffeePrice;break;case Grand:finalPrice _e.CoffeePrice 3;break;case Venti:finalPrice _e.CoffeePrice 6;break;}_customer.Bill finalPrice;}
}最后我们还需要触发这个事件因此我们在Customer中定义一个Order函数来触发委托。只需要为委托传入类型匹配的参数即可触发所有绑定的事件处理器EventHandler
public class EventEx : MonoBehaviour
{Customer customer new Customer();Waiter waiter new Waiter();OrderEventArgs e new OrderEventArgs();private void Start(){customer.OnOrder waiter.TakeAction;customer.Order();// 输出结果I have to pay:64customer.PayTheBill();}
}public delegate void OrderEventHandler(Customer _customer, OrderEventArgs _e);public class Customer
{public float Bill { get; set; }public void PayTheBill(){Debug.Log(I have to pay: this.Bill);}private OrderEventHandler orderEventHandler;public event OrderEventHandler OnOrder{add{orderEventHandler value;}remove{orderEventHandler - value;}}public void Order(){// 为两杯咖啡触发了两次点单事件if(orderEventHandler ! null){OrderEventArgs e new OrderEventArgs();e.CoffeeName Mocha;e.CoffeeSize Tall;e.CoffeePrice 28;orderEventHandler(this, e);OrderEventArgs e1 new OrderEventArgs();e1.CoffeeName Latte;e1.CoffeeSize Venti;e1.CoffeePrice 30;orderEventHandler(this, e1);}}
}小结
小结一下刚才讲的内容: 首先我们应当确定好事件的拥有者和响应者之间的关系例如顾客和服务员因为我们需要顾客点单服务员才会有反应。因此顾客是事件的拥有者当其点单之后服务员作为响应者去响应这个事件。
然后需要定义事件在成员外部定义事件的FooEventHandler的委托约束并定义内部sender和FooEventArgs的类型。在事件进行完整定义的时候需要在成员内部事件拥有者定义委托fooEventHandler和事件OnFoo包括对添加器Add和移除器Remove的定义。
最后将事件与响应Handler绑定想要使用的时候就直接调用即可。 事件的简略声明
通常事件的声明往往使用更简略的声明方式。简略声明的好处是提供了一些特殊的语法糖。 public event OrderEventHandler OnOrder;public void Order(){if(OnOrder ! null){OrderEventArgs e new OrderEventArgs();e.CoffeeName Mocha;e.CoffeeSize Tall;e.CoffeePrice 28;OnOrder(this, e);OrderEventArgs e1 new OrderEventArgs();e1.CoffeeName Latte;e1.CoffeeSize Venti;e1.CoffeePrice 30;OnOrder.Invoke(this, e1);}}我们修改一下顾客类中的事件声明和代码。发现几个特点
OnOrder直接用event关键字声明了一个事件而不是先声明一个委托再声明事件中对委托的添加器和移除器的定义。Order直接用!来比较委托是否为空我们说事件的操作符只能是-和在此处却可以使用!甚至仅限成员函数内部这也是迫不得已因为我们没有定义委托所以直接用事件来代替委托进行操作。然而委托真的没有被定义吗只是编译器内部帮我们定义好了一个委托我们看不到而已。在触发事件的时候不仅用通常的方法OnOrder(this, e);来触发还可以使用OnOrder?.Invoke(this, e1);和OnOrder.Invoke(this, e1);来进行触发更加灵活了。
从上述代码来看简略声明的事件更灵活更强大。
此外由于简略声明事件的定义格式public event OrderEventHandler OnOrder;不要误以为它是一个字段只是语法糖的存在让它看起来长得像一个字段。实际上还是一个事件。 泛型委托定义下的事件
除了常态的委托类型之外定义事件我们也可以用到泛型委托例如微软官方提供的泛型委托
public delegate void EventHandlerTEventArgs(object sender, TEventArgs e);所以我们也可以定义一个泛型委托例如不止顾客点单服务员自己也可以给自己点咖啡在不继承同一个基类的情况下就可以用泛型委托来接受不同类型对象的事件响应。
public delegate void OrderEventHandlerTsender(Tsender sender, OrderEventArgs _e);为什么使用事件
如果我们将下列事件中的event关键字去掉可以正常处理上述代码吗答案是可以
public event OrderEventHandler OnOrder;//变成了委托
public OrderEventHandler OnOrder;既然如此我们为什么要使用事件呢 因为委托的封装不够严密不符合我们对于事件的想象。我们可以用如下方式去访问类中public的委托
customer1.OnOrder(customer1,e1);
customer2.OnOrder(customer1,e2);在上述代码中顾客1为自己点了一份名为e1的订单这是没有问题的。 但是顾客2也为顾客1点了一份名为e2的订单顾客2直接访问了顾客1中public出来的委托字段一般而言我们不希望通过这样的方式去为其他类触发事件。这会造成一些逻辑上的错误。使用事件就可以把其对应的委托封装起来避免一些奇怪的用法。
事件的存在就是为了阻止一些委托调度的“非法操作”更安全更有约束。