本文是学习 博客C# 中的委托和事件(详解) 的心得 ,博客原文为:http://www.cnblogs.com/SkySoot/archive/2012/04/05/2433639.html
委托和事件的用法:
使用委托和事件的目的,在这个例子中,加热器(heater)只负责加热,而报警器(alamter)负责报警和显示器(viewer)负责显示,而加热器如何告诉报警器和显示器?这就需要用到委托(事件)。
使用到了观察者模式
为什么委托定义的返回值通常都为 void ?
尽管并非必需,但是我们发现很多的委托定义返回值都为 void,为什么呢?这是因为委托变量可以供多个订阅者注册,如果定义了返回值,那么多个订阅者的方法都会向发布者返回数值,结果就是后面一个返回的方法值将前面的返回值覆盖掉了,因此,实际上只能获得最后一个方法调用的返回值。可以运行下面的代码测试一下。除此以外,发布者和订阅者是松耦合的,发布者根本不关心谁订阅了它的事件、为什么要订阅,更别说订阅者的返回值了,所以返回订阅者的方法返回值大多数情况下根本没有必要
using System;
//编码规范
//1. 委托类型的名称都应该以 EventHandler 结束。
//2. 委托的原型定义:有一个void 返回值,
//并接受两个输入参数:一个Object 类型,一个EventArgs 类型(或继承自EventArgs)。
//3. 事件的命名为委托去掉 EventHandler 之后剩余的部分。
//4. 继承自 EventArgs 的类型应该以EventArgs 结尾。
namespace testEvent
{
class MainClass
{
public static void Main1 (string[] args)
{
Heater heater = new Heater ();
Alamter a = new Alamter ();
heater.sendTempure -= a.OnAlam;
heater.sendTempure += a.OnAlam;
heater.sendTempure += new Viewer ().OnView;
heater.boillWater ();
}
}
//监视对象,里面含有被监视的内容:温度
public class Heater{
public String Name {
get;
set;
}
public int Tempurea {
get;
set;
}
//定义成void,是为了避免多个事件注册而返回值被覆盖,
//如果有这种需求,可以用特殊方法来避免被多次注册。
//Object sender 用于监视者强转成监视对象,获取到监视对象的属性,如Name。
//
public delegate void sendTempureEventHander (Object sender,BoilEventArgs e);
public event sendTempureEventHander sendTempure;
//这个地方必须是空的,因为真正执行任务的是Alamter和Viewer这两个监视者类,
//而监视对象只是用这个handler来占位而已
//要真正使sendTempure内容不为空,需要在真正使用时,绑定监视者的具体方法。
public void boillWater(){
for(int i=0;i<100;i++){
Console.WriteLine ("开始烧水");
if(i>90){
BoilEventArgs boilEventArgs = new BoilEventArgs (i);
sendTempure (this, boilEventArgs);
}
}
}
}
//承自 EventArgs 的类型应该以EventArgs 结尾。
public class BoilEventArgs:EventArgs {
public int Tempure {
get;
set;
}
public BoilEventArgs(int tempure){
this.Tempure = tempure;
}
}
//监视者
//用于报警的类
public class Alamter{
//这里还有一个约定俗称的规定,就是订阅事件的方法的命名,通常为“On 事件名”,比如这里的OnNumberChanged
public void OnAlam(Object sender, BoilEventArgs e){
Console.WriteLine ("名字为"+((Heater)sender).Name+"温度达到了:"+e.Tempure+"警报器开始报警");
}
public static void OnAlamt(Object sender, BoilEventArgs e){
Console.WriteLine ("名字为"+((Heater)sender).Name+"温度达到了:"+e.Tempure+"警报器开始报警");
}
}
//用于显示的类
public class Viewer{
public void OnView(Object sender, BoilEventArgs e){
Console.WriteLine ("名字为"+((Heater)sender).Name+"温度达到了:"+e.Tempure+"显示器开始显示");
}
}
}
如何让事件只允许一个客户订阅?
using System;
namespace OneEvent
{
public class OneEventPusher{
public String Name{ get; set;}
public delegate void ChangeValueEventHandler(Object sender, ChangeEventArgs e);
private event ChangeValueEventHandler changeValue;
public void Register(ChangeValueEventHandler handler){
changeValue = handler;
}
//如果changValue为空,-=操作也不会报错
public void UnRegister(ChangeValueEventHandler handler){
changeValue -= handler;
}
public void doWork(int i){
//因为在Register 方法时使用的是=,每次changeValue只会注册一个方法,所以,可能会发生UnRegister时,changeValue为空,故需要判断一下
if(changeValue!=null){
ChangeEventArgs args = new ChangeEventArgs (i);
changeValue(this,args);
}
}
}
public class ChangeEventArgs:EventArgs{
public int passValue {
get;
set;
}
public ChangeEventArgs(int pass_value){
this.passValue = pass_value;
}
}
public class OneEventWorker1{
public void OnChange(Object sender,ChangeEventArgs changerArgs){
Console.WriteLine ("I am worker 1!");
Console.WriteLine (((OneEventPusher)sender).Name);
Console.WriteLine (changerArgs.passValue);
}
}
public class OneEventWorker2{
public void OnChange(Object sender,ChangeEventArgs changerArgs){
Console.WriteLine ("I am worker 2!");
Console.WriteLine (((OneEventPusher)sender).Name);
Console.WriteLine (changerArgs.passValue);
}
}
class MainClass
{
public static void Main2 (string[] args)
{
OneEventPusher one = new OneEventPusher ();
one.Name ="Test";
OneEventWorker1 work1 = new OneEventWorker1 ();
one.Register (work1.OnChange);
one.UnRegister (work1.OnChange);
//one.UnRegister (work2.OnChange);
one.doWork (1);
}
}
}
如何使用委托异步调用观察者:
using System.Threading;
using System;
using System.Runtime.Remoting.Messaging;
namespace UnSynzDemo{
public delegate void Count();
class MainClass
{
//同步执行
public static void Main3 (string[] args)
{
Console.WriteLine ("Thread name is ---"+Thread.CurrentThread.Name);
Count count = new Count (new CountClass ().getCounts);
count() ;
Console.WriteLine ("main Class is over");
}
//异步执行
public static void Main (string[] args)
{
if (Thread.CurrentThread.IsThreadPoolThread) {
Thread.CurrentThread.Name = "this is ThreadPoolThread";
} else {
Thread.CurrentThread.Name = "this is mainThread";
}
Console.WriteLine ("Thread name is ---"+Thread.CurrentThread.Name);
Counter counter = new Counter ();
counter.doWork ();
Thread.Sleep (5000);
Console.WriteLine ("main Thread is over");
}
}
class CountClass{
public void getCounts(){
int count = 0;
if (Thread.CurrentThread.IsThreadPoolThread) {
Thread.CurrentThread.Name = "this is ThreadPoolThread";
} else {
Thread.CurrentThread.Name = "this is mainThread";
}
Console.WriteLine ("CountClass Thread Name is "+ Thread.CurrentThread.Name);
for(int i =0;i<10;i++){
count += i;
Thread.Sleep (TimeSpan.FromMilliseconds(300));
}
Console.WriteLine (count);
}
public int OnCounts(int c){
if (Thread.CurrentThread.IsThreadPoolThread) {
Thread.CurrentThread.Name = "this is ThreadPoolThread";
} else {
Thread.CurrentThread.Name = "this is mainThread";
}
Console.WriteLine ("CountClass Thread Name is "+ Thread.CurrentThread.Name);
int totolCount = 0;
for(int i =0;i<c;i++){
totolCount += i;
}
return totolCount;
}
}
//用于执行异步操作的delegate
public delegate int CountNumEventHandler(int c);
//系统自带的delegate,用于绑定回调函数的方法,参数为固定的IAsyncResult类型
//public delegate void AsyncCallback(IAsyncResult ar);
class Counter{
public void doWork(){
Console.WriteLine ("doWork Thread Name is "+ Thread.CurrentThread.Name);
//调用BeginInvoke前面的参数和delegate的参数一致,
//而后面两个参数,第一个参数表示回调函数,第二个参数表示传递的参数(两个参数都可以为null值)
//BeginInvoke的返回值为
int c = 10;
string data = "123";
CountNumEventHandler handler = new CountNumEventHandler (new CountClass().OnCounts);
AsyncCallback callBack = new AsyncCallback (CounterCallBack);
//后面两个参数都可以为null值
//handler.BeginInvoke (c,null,null);
//delegate 参数为空的情况
//handler.BeginInvoke(null,null);
//可以直接获取相应的运行结果和传递的参数
//IAsyncResult IResult = handler.BeginInvoke(c,callBack,data);
//IAsyncResult IResult = handler.BeginInvoke (c,null,data);
//AsyncResult result = (AsyncResult)IResult;
//int i = handler.EndInvoke (result);
//Console.WriteLine (result.AsyncState);
//Console.WriteLine (i);
//Console.WriteLine ("do Working is over");
//也可以在回调函数中获取相应的运行结果和传递的参数
IAsyncResult IResult = handler.BeginInvoke(c,callBack,data);
}
//回调函数
public void CounterCallBack(IAsyncResult ar){
Console.WriteLine ("callBack Thread Name is "+ Thread.CurrentThread.Name);
//转为AsyncResult,此时就可以获取相应的属性了
AsyncResult result = (AsyncResult)ar;
CountNumEventHandler handler = (CountNumEventHandler)result.AsyncDelegate;
//获取方法的返回值
int rtn = handler.EndInvoke(result);
Console.WriteLine ("CallBack return is "+rtn);
string data = (string)result.AsyncState;
Console.WriteLine ("CallBack passData is "+data);
Console.WriteLine (data);
Console.WriteLine ("CallBack Working is over");
}
}
}
最终异步调用的结果如下: