首页 > Python基础教程 >
-
C#教程之【C#进阶】委托那些事儿(二)
二、传统的委托
接下来讲一讲方法参数。下面以“餐馆服务员为客户下单”[2]的事件作为描述。一般对事件的做法分3个部分:
1. 方法参数 EventArgs,一般用于传送数据。在本例场景中
public delegate void OrderEventHandler(Customer cus, OrderEventArgs e); public class OrderEventArgs : EventArgs // 习惯把xxEventArgs 继承于C#自带的EventArgs { public string DishName { get; set;} // 菜名 public string Size { get; set;} // 份量 }
2 . 触发事件的对象
// 下单的事件是Customer对象拥有的,∴写在Customer类当中
public class Customer { private OrderEventHandler orderEventHandler; public event OrderEventHandler Order { // event类型是用来操作“方法类”这个盒子的 add { this.orderEventHandler += value;} // add是事件处理器的添加器 remove { this.orderEventHandler -= value;} } public void ThinkForOrder () // 顾客下单 { if (this.orderEventHandler != null ) { orderEventArgs e = new orderEventArgs{ DishName = "Soup", Size = "Large"}; this.orderEventHandler.Invoke(this, e); // this指此类实例化的customer } } }
3 . 执行的方法
在主函数中,为customer对象的Order事件订阅waiter.Action,客户的下单,需要由服务员行动。
cus.Order += waiter.Action;
即有:
public class Waiter
{ public void Action(Customer cus, OrderEventArgs e) { ... } }
总结上文:
Main |
Customer cus = new Customer(); Waiter wai = new Waiter(); cus.Order += wai.Action; cus.ThinkForOrder(); Console.WriteLine("the customer will pay {0}.", cus.BillPrice); |
Customer |
事件对象对eventHandler方法类的订阅 |
Waiter |
public class Waiter { internal void Action(Customer customer, OrderEventArgs e) // internal可改为public { Console.WriteLine("Waiter will serve Mr.{0} {1}." , customer.Name, e.DishName); customer.Bill += e.Price; } } |
三、小结
如果把事件写成委托型字段的话:
- 假设有一客人badGuy,并且badGuy.Order += waiter.Action;,那么如果badGuy.Order.Invoke(),即会破坏参数e,或者参数customer。
例如,badGuy不给自己点菜,点到了customerA上,badGuy.Order.Invoke(customerA, e2);
事件:
- 使逻辑、对象关系更加安全,防止“借刀杀人”。
- 只能写在+=或-=的左边。避免了委托被直接invoke调用。
(委托字段可能在public当中被滥用,所以微软推出Event这种成员。)
- 本质:委托字段的包装器;
对委托字段的访问仅起限制作用,仅暴露add、remove事件处理器的功能。
- 参数:一个表示发送者,e表示发送的消息/数据/内容
- 规定:事件触发必须由事件Foo拥有者自己去发送信息。
触发事件的方法一般命名为:OnFoo,意为事出有因。
注意:OnFoo的访问级别一定是protected,若为public又可“借刀杀人”了。
首尾呼应:
属性不是字段——很多时候,属性是字段的包装器,保护字段不被滥用。包装器永远不可能是包装的东西。