zoukankan      html  css  js  c++  java
  • C#基础之事件(2)

    在“C#基础之事件(1)”中已对事件有了一个大概,这里对事件进行更深入的学习。

    本节按以下内容展开:

    1.事件拥有者与事件响应者的关系;

    2.事件订阅的多种写法;

    3.事件的订阅和取消订阅;

    4.多事件返回值的获取;

    1.事件拥有者与事件响应者的关系

    以下按照最常见到最不常见的关系列出来,并用示例展示:

     结合这四副图,分析下内存关系。所谓“XX者"就是指一个对象,就是new class(),对象所包含的事件、事件处理方法都是对象的成员。有了这个认识,在此用A图,这个最常用的关系来写一段“伪代码”。

    public class 拥有者类
    {
        public event 事件类型 事件成员变量;
    
        public void 某个方法(参数)
        {
            。。。
            事件成员变量(参数);//此处是调用事件处理方法
            。。。
        }
    }
    
    public class 响应者类
    {
        private 拥有者类 _拥有者对象;
    
        public 响应者类(拥有者类 拥有者对象)
        {
            this._拥有者对象=拥有者对象;
            _拥有者对象.事件成员变量+=事件处理方法;
        }
    
        public string 事件处理方法(参数)
        {
            。。。
        }
    }

    由上面代码可知,响应者中包含拥有者时,可以通过构造方法传参形式包含进去,实际应用中不限于这种形式,还可以是new 一个拥有者对象、静态类等方式,只要是能包含进去的都是可行的。其它几个关系就更简单了。这些关系搞清楚了,才能“下笔如有神”。

    下面是一个实际代码示例(网络资源非原创):

    从program类中可以看到,通过调用事件拥有者dg的OnAlarm方法触发了事件,最终是调用了响应者host类的catch方法,与上图中的A的关系模型一样。

    class Program
    {
        static void Main(string[] args)
        {
            Dog dg = new Dog();
            Host ht = new Host(dg);
            DateTime now = new DateTime(2015, 8, 26, 23, 59, 40);
            DateTime end = new DateTime(2015, 8, 27, 0, 0, 0);
            Console.WriteLine("时间快接近深夜0时~~~~");
            while (now < end)
            {
                Console.WriteLine(now);
                Thread.Sleep(1000);
                now = now.AddSeconds(1);
            }
            //午夜零点小偷到达,看门狗引发Alarm事件
            Console.WriteLine("月黑风高的午夜: " + now);
            Console.WriteLine("小偷悄悄地摸进了主人的屋内... ");
            //自定义参数
            UserEventArgs e = new UserEventArgs(3);
            dg.OnAlarm(e);
            Console.WriteLine("请按任何键退出~");
            Console.ReadKey();
        }
    }
    
    //自定一个事件参数类
    class UserEventArgs : EventArgs
    {
        private int iEventArgs;
        public int Args
        {
            set { iEventArgs = value; }
            get { return iEventArgs; }
        }
        public UserEventArgs(int e)
        {
            iEventArgs = e;
        }
    }
    
    class Dog
    {
        //1.声明关于事件的委托;
        public delegate void AlarmEventHandler(object sender, UserEventArgs e);
        //2.声明事件
        public event AlarmEventHandler Alarm;
        //3.编写引发事件的函数;
        public void OnAlarm(UserEventArgs e)
        {
            if (Alarm != null)
            {
                for (int k = 0; k < e.Args; k++)
                {
                    Console.WriteLine("汪汪~~");
                }
                Alarm(this, e);
            }
        }
    }
    class Host
    {
        //主人接收到信息引发的动作
        public void Catch(object sender, UserEventArgs e)
        {
            Console.WriteLine("NND小偷,别跑~");
        }
        public Host(Dog d)
        {
            d.Alarm += new Dog.AlarmEventHandler(Catch);
        }
    }

    2.事件订阅的多种写法

    在“C#基础之事件(1)”中,用

     _A.GetStr += OnGetStr;

    来订阅事件,但在上例中用

    d.Alarm += new Dog.AlarmEventHandler(Catch);

    这种形式来订阅,这两种有什么不同吗?,答案是:没有。前一种是C#2.0的方法,也是我们现在常用的方法;后一种是C#1.0的方法。大致在网络上查找了下,绝大部分示例都是用的后一种写法。在实际项目代码中,用的多的还是前一种写法,毕竟更简洁,看起来也舒服。

    从后一种写法可知,其本质是new 了一个委托,并把方法当作委托的参数,参见前面“委托”章节,委托有多种写法,那么当然,除上面两种写法外,事件的订阅就同样有多种简略写法,这时的事件处理逻辑就可以直接写在{}中,而不用单独写成一个方法,实际用哪种方法看个人编码习惯和团队规范要求。现展示如下:

    lambda表达式写法
    Button1.Click+=(s,e)=>{
    
    };
    
    delegate写法
    Button1.Click+=delegate(object sender,EventArgs e) {
    
    };
    
    匿名事件处理
    Button1.Click+=delegate {
    
    };

    3.事件的订阅和取消订阅

    事件的订阅用“+=”,取消订阅用“-=”,实际是调用了Add和remove的两个方法,想了解详情请参考《CLR via C#》,这里只从应用角度来说明。需要强调一点:如果是在非构造方法中订阅的事件,在适当的时候需取消订阅事件,如果没有取消当再次调用事件订阅时,会第二次订阅同一事件,这样事件处理方法就会执行第二次,如果执行n 次就会触发n次事件处理方法。

    4.多事件返回值的获取

    多事件返回值可以通过GetInvocationList方法获取,这个在实际中应用中不是很多,但有应用到。以下从网上找了个示例:

     private void button1_Click(object sender, EventArgs e)
            {
                int Number = 200;
                Publishser pub = new Publishser();
                Subscriber1 sub1 = new Subscriber1();
                Subscriber2 sub2 = new Subscriber2();
                Subscriber3 sub3=new Subscriber3();
                pub.NumberChanged += sub1.OnNumberChanged;
                pub.NumberChanged += sub2.OnNumberChanged;
                pub.NumberChanged += sub3.OnNumberChanged;
                pub.DoComething(Number);
            }
    
        class Publishser
        {
            public delegate int DemoEventHandler(int num);
            public event DemoEventHandler NumberChanged;
            public void DoComething(int temp)
            {
                if (NumberChanged != null)
                {
                    Delegate[] delArray = NumberChanged.GetInvocationList();  
                    foreach (Delegate del in delArray)
                    {
                        DemoEventHandler method = (DemoEventHandler)del;
                        temp = method(temp);
                    }
                }
                MessageBox.Show(temp.ToString());
            }
        }
    
        class Subscriber1
        {
            public int OnNumberChanged(int num)
            {
                MessageBox.Show("调用了Subscriber1类,num值为:"+ num);
                return num + 100; ;
            }
        }
    
        class Subscriber2
        {
            public int OnNumberChanged(int num)
            {
                MessageBox.Show("调用了Subscriber2类 num值为:"+num);
                return num+100;
            }
        }
    
        class Subscriber3
        {
            public int OnNumberChanged(int num)
            {
                MessageBox.Show("调用了Subcriber3类,num值为:"+num);
                return num+100;
            }
        }
    
    运行得到的结果是:
    
                                  调用了Subscriber1类,num值为:200
    
                                  调用了Subscriber2类,num值为:300
    
                                  调用了Subscriber3类,num值为:400
    
                                  500

    总结:事件的应用非常广泛,需作为重点来学习。

  • 相关阅读:
    tomcat并发个题-未解决
    tengine安装
    nginx获得自定义参数
    nginx限流
    树形背包——hdu1561
    树形dp专题
    单调队列——P1725 琪露诺
    单调队列,dp——POJ
    记忆化搜索——HDU
    区间dp——POJ
  • 原文地址:https://www.cnblogs.com/qcst123/p/11657691.html
Copyright © 2011-2022 走看看