3. 通过方法避免风险
很自然想到采用类似Get和Set的方式避免上面的问题。既然委托可以像变量一样赋值,那么也可以通过参数来传值,将一个方法作为参数传递。、
public class HRWithAddRemove { private SendDelegate sendDelegate; public void AddDelegate(SendDelegate sendDelegate) { this.sendDelegate += sendDelegate; //如果需要限制最多绑定一个,此处可以用=号 } public void RomoveDelegate(SendDelegate sendDelegate) { this.sendDelegate -= sendDelegate; } public void SendMessage(string msg) { sendDelegate(msg); } }
经过改造后的HR,SendDelegate方法被设置为了private,之后只能通过Add和Remove的方法进行方法绑定。
4.模拟多播委托机制
通过上面委托的表现来看,委托就像是保存了一个相同方法名的集合 List<SendDelegate> ,可以向集合中添加或移除方法,当调用这个委托的时候,会逐一调用该集合中的各个方法。
例如下面的代码( 注意这里假设SendDelegate只对应一个方法 ):
public class HR1 { public void Delegate(SendDelegate sendDelegate) { sendDelegateList = new List<SendDelegate> { sendDelegate }; //对应= } public void AddDelegate(SendDelegate sendDelegate) { sendDelegateList.Add(sendDelegate); //对应+= } public void RomoveDelegate(SendDelegate sendDelegate) { sendDelegateList.Remove(sendDelegate);//对应-= } public List<SendDelegate> sendDelegateList; public void SendMessage(string msg) { foreach (var item in sendDelegateList) { item(msg); } } }
二、C#1.0 引入事件
1.简单事件
如果既想使用-=和+=的方便,又想避免相关功能开闭的风险怎么办呢?可以使用事件:
public class HRWithEvent { public event SendDelegate sendDelegate; public void SendMessage(string msg) { sendDelegate(msg); } }
只是将SendDelegate前面添加了一个event标识,虽然它被设置为public,但如下代码却会给出错误提示: 事件“HRWithEvent.sendDelegate”只能出现在 += 或 -= 的左边(从类型“HRWithEvent”中使用时除外)
hr.sendDelegate = senderZS.Send; hr.sendDelegate("偷偷的发一条");
2.事件的访问器模式
上文为委托定义了Add和Remove方法,而事件支持这样的访问器模式,例如如下代码:
public class CustomerWithEventAddRemove { private event SendDelegate sendDelegate; public event SendDelegate SendDelegate { add { sendDelegate += value; } remove { sendDelegate -= value; } } public void SendMessage(string msg) { sendDelegate(msg); } }
可以像使用Get和Set方法一样,对事件的绑定与移除进行条件约束。
3. 控制绑定事件的执行
当多个委托被绑定到事件之后,如果想精确控制各个委托的运行怎么办,比如返回值(虽然经常为void)、异常处理等。
第一章第4节通过一个List<SendDelegate> 模拟了多播委托的绑定。 会想到如果真能循环调用一个个已绑定的委托,就可以精确的进行控制了。那么这里说一下这样的方法:
public class HRWithEvent { public event SendDelegate sendDelegate; public void SendMessage(string msg) { //sendDelegate(msg); 此处不再一次性调用所有 if (sendDelegate != null) { Delegate[] delegates = sendDelegate.GetInvocationList(); //获取所有已绑定的委托 foreach (var item in delegates) { ((SendDelegate)item).Invoke(msg); //逐一调用 } } } }
这里通过Invoke方法逐一调用各个Delegate,从而实现对每一个Delegate的调用的控制。若需要异步调用,则可以通过BeginInvoke方法实现(.NET Core之后不再支持此方法,后面会介绍。)
((SendDelegate)item).BeginInvoke(msg,null,null);
4. 标准的事件写法
.NET 事件委托的标准签名是:
void OnEventRaised(object sender, EventArgs args);
返回类型为 void。 事件基于委托,而且是多播委托。 参数列表包含两种参数:发件人和事件参数。 sender 的编译时类型为 System.Object。
第二种参数通常是派生自 System.EventArgs 的类型(.NET Core 中已不强制要求继承自System.EventArgs,后面会说到)。
将上面的例子修改一下,改成标准写法,大概是下面代码的样子:
public class HRWithEventStandard { public delegate void SendEventHandler(object sender, SendMsgArgs e); public event SendEventHandler Send; public void SendMessage(string msg) { var arg = new SendMsgArgs(msg); Send(this,arg); //arg.CancelRequested 为最后一个的值 因为覆盖 } } public class SendMsgArgs : EventArgs { public readonly string Msg = string.Empty; public bool CancelRequested { get; set; } public SendMsgArgs(string msg) { this.Msg = msg; } }