认识C#中的委托和事件

本文是学习 博客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");
        }
    }
}


最终异步调用的结果如下:


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值