C#学习(10)-----事件

一、事件的基本概念

事件是什么呢?他是一个东西,得能够发生,发生了就会给其他的东西通知。

事件是类的成员。它使类或对象具有通知别的类或对象的能力。

而当然,不仅仅有通知。

有些事件,例如说手机响铃,会伴随着其他的消息,比如谁谁谁领导通知你开会,家人跟你聊天什么的。这种伴随着事件传达的信息称为事件参数

手机响铃会有很多种可能,而有些事件,其发生与通知本身就蕴含了一些信息,没有必要再给你其他的事件参数。比如红灯停绿灯行。

因而,事件的功能=通知+可选的事件参数。它用与类或对象之间的动作协调与信息传递。

/*感觉在C语言中,这个功能是通过使用flag啊isXX等等标志的布尔值或者对数值的判断功能来实现的?*/

事件通知到的那个东西,同时也是接受事件通知、关心事件通知、行动,的那个东西,叫作事件的订阅者。它们接收到事件的信息后采取行动称为响应事件/处理事件。它们处理事件时所进行的行动称为事件处理器

将现实中的具体实物抽象化为事件,我们具有一个“事件模型”的原理。它由两个“5”组成:

其一,发生-->响应的五个部分:如闹钟响了我起床,不只有“闹钟”、“响了”、“我”、“起床”这五个元素,它还包含了“我”关心着“闹钟”这个订阅关系。

其二,发生-->响应的五个动作:(1)我有一个事件(2)有一个人或一群人关心着我这个事件(3)我发生了(4)我依次通知他们并且说明该做的事情(5)他们根据事件参数处理事件

注:

  为了防止事件模型逻辑混乱的场面,对于事件的逻辑设置,人类总结出了MVP.MVC、MVVM等设计模式,是事件模式更高级有效的方法。

  在日常生活中,声明事件机会较少,更多是使用已有的事件,故更重要学会怎么用。

二、事件的应用

1.事件模型的五个组成部分

(1)事件的拥有者/事件的source(event source,对象)

        手机响铃的手机

(2)事件成员(event)

        事件本身。

        它使类或对象具有通知别的类或对象的能力。

        是被动的工具

        只有在事件的拥有者完成内部逻辑后触发才能发挥通知的作用

(3)事件的订阅者(event subscriber,对象)

        被通知到的类或对象

(4)事件处理器(event handler,成员)————本质上是一个回调方法

(5)事件订阅——把事件处理器与事件关联在一起,本质上是一种以委托类型为基础的约定

        解决了三个问题:

        有谁被通知到,拿什么样的事件处理器处理这个事件,事件的响应者具体拿哪个方法处理这个事件

        C#规定,用于订阅事件的事件处理器必须与事件遵守同一个约定,约定约束了事件能够发送什么样的消息给处理器,也约束处理器能够处理什么样的消息。

2.常用的事件订阅者和拥有者的组合

第一种,事件的拥有者和事件的订阅者处在两个不同的类。这是最基础的一个组合。

第二种,事件的响应者和拥有者是同一个对象。

第三种是最广泛的,事件的拥有者是事件的响应者的一个字段成员

比如说WPF的button。button是事件的拥有者,窗口是事件的响应者,button是窗口的一个字段成员。

3.使用事件

namespace ConsoleAppPractice
{
    class Program
    {
        static void Main()
        {
            Boy boy = new Boy();//事件响应者
            Girl girl = new Girl();
            Timer timer = new Timer();//事件拥有者

            timer.Interval = 500;
            //事件订阅、事件成员
            timer.Elapsed += boy.Action;//Elapsed是timer的一个事件。+=操作符用于让对象订阅一个事件
            timer.Elapsed += girl.Action;
            timer.Start();
            Console.ReadLine();
        }
    }

    class Boy
    {
        //可以使用alt+enter让系统帮你创造一个这个事件处理器
        //事件处理器
        internal void Action(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine("I'm running happily!");
        }
    }

    class Girl
    {
        internal void Action(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine("I'm drawing happily!");
        }
    }
}

感觉很像是多播委托

接下来,我们看看具体的三种模式的代码实例。

事件的拥有者跟事件的响应者是两个不同的对象

/*
代码功能:让form显示出来,点击一下后关闭
*/
namespace ConsoleAppPractice
{
    class Program
    {
        static void Main()
        {
            Form form = new Form();//事件的拥有者
            Controller controller = new Controller(form);//事件的响应者
            form.ShowDialog();
        }
    }
    class Controller
    {
        private Form form;
        public Controller(Form form)
        {
            this.form = form;
            this.form.Click += this.Action;//订阅事件
        }
        private void Action(object sender, EventArgs e)//事件处理器。注意此处事件参数不同于上一例
        {
            this.form.Close();
        }
    }
}

注意,此处如果进行如下修改:

            Form form = new Form();
            Controller controller = new Controller();
            form.ShowDialog();




        public Controller()
        {
            this.form = new Form();
            this.form.Click += this.Action;//订阅事件
        }

是不能完成任何功能的。这也体现了第一种模式和第三种模式的区别。虽然都是在构造器里进行初始化和事件订阅,但是由于第一个模式,是用一个对象去响应另一个对象,故必须传递事件拥有者的地址,才能让事件被正确订阅;第三个模式只有自己一个对象,所以只需创建自己的实例即可。

第三个模式例子中的私有字段成员button和textBox之所以能被外界访问,我初步猜想是由于:

            this.Controls.Add(this.button);
            this.Controls.Add(this.textBox);

这两条语句。

我们仍希望使用Form这个类,因而我们需要对Form这个类添加事件处理器。

但是Form这个类是微软自带的类,不能被修改。所以,我们可以使用拓展方法或者派生类,来为其添加方法。

具体代码如下:

//使用派生类
namespace ConsoleAppPractice
{
    class Program
    {
        static void Main()
        {
            MyForm form = new MyForm();//事件的拥有者、订阅者
            form.Click += form.Action;//订阅事件
            form.ShowDialog();
        }
    }
    class MyForm : Form
    {
        internal void Action(object sender, EventArgs e)//事件处理器。事件参数于上一例相同
        {
            this.Text = "Hello World!";
        }
    }
}
//使用扩展方法
namespace ConsoleAppPractice
{
    class Program
    {
        static void Main()
        {
            Form form = new Form();
            form.Click += form.Action;
            form.ShowDialog();
        }
    }
    static class FormExtension
    {
        internal static void Action(this Form form,object sender, EventArgs e)
        {
            form.Text = "You clicked me just now.>///<";
        }
    }
}

事件的拥有者是事件的响应者的一个字段成员。

namespace ConsoleAppPractice
{
    class Program
    {
        static void Main()
        {
            MyForm form = new MyForm();
            form.ShowDialog();
        }
    }
    class MyForm : Form
    {
        private Button button;
        private TextBox textBox;
        public MyForm()
        {
            this.button = new Button();
            this.textBox = new TextBox();
            this.Controls.Add(this.button);
            this.Controls.Add(this.textBox);
            button.Top = 50;
            button.Click += this.ClickEvent;
        }
        private void ClickEvent(object sender, EventArgs e)
        {
            this.textBox.Text = "Hello!You cliked me just now.";
        }
    }
}

下面我们来看例子,通过其看事件的自定义声明。

这例子的实际背景:一个顾客走进来,服务员迎接,并问要点什么菜。顾客说明菜名和尺寸后,服务员接受订单并计算账单价格,随后顾客坐下来吃饭,最后付款。

运行效果:

由之可见,需要两个事件。一个是顾客走进来,服务员迎接;一个是顾客点菜,服务员计算账单。这两个事件当然C#没有提供,所以需要我们自己声明。

具体代码如下:

using System;
using System.Windows.Forms;
using System.Threading;

namespace ConsoleAppPractice
{
    public delegate void WalkInEventHandler(Customer customer, EventArgs e);
    public delegate void OrderEventHandler(Customer customer, OrderEventArgs dish);
    class Program
    {
        static void Main()
        {
            Thread.Sleep(2000);
            Sever sever = new Sever();
            Customer customer = new Customer();
            customer.Action(sever);
        }
    }

    public class Customer
    {
        public int Bill { get; set; }

        public void Action(Sever sever)//实现代码基本逻辑
        {
            this.WalkIn += sever.WelcomeAction;
            this.Walking();
            Thread.Sleep(3000);
            this.Order += sever.OrderAction;
            this.OrderDish();
            this.Eating();
            this.PayTheBill(this.Bill);
        }

        private WalkInEventHandler walkInEventHandler;//声明事件1:走进来了
        public event WalkInEventHandler WalkIn
        {
            add
            {
                this.walkInEventHandler += value;
            }
            remove
            {
                this.walkInEventHandler -= value;
            }
        }
        protected void Walking()//事件触发的内部逻辑,使用protected保护
        {
            Console.WriteLine("I am walking in!");
            Thread.Sleep(2000);
            this.Sit();
            EventArgs e = new EventArgs();
            this.walkInEventHandler.Invoke(this,e);//触发事件
        }

        public void Sit()
        {
            Console.WriteLine("I sat.");
        }

        private OrderEventHandler orderEventHandler;//声明事件2:顾客点单
        public event OrderEventHandler Order
        {
            add
            {
                this.orderEventHandler += value;
            }
            remove
            {
                this.orderEventHandler -= value;
            }
        }
        protected void OrderDish()//事件触发内部逻辑,得用protected保护
        {
            Console.WriteLine("I will make an order.");
            OrderEventArgs dish = new OrderEventArgs();
            this.orderEventHandler.Invoke(this, dish);
            Sever.CountAction(this, dish);
        }
        public void Eating()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("I am eating......");
                Thread.Sleep(1000);
            }
        }
        public void PayTheBill(int bill)
        {
            Console.WriteLine("I will pay ${0}",bill);
        }

    }

    public class Sever
    {
        public static void CountAction(Customer customer, OrderEventArgs dish)
        {
            int price = 0;
            switch (dish.DishName)
            {
                case "FriedFish":
                    price = 10;
                    break;
                case "ChickenSoup":
                    price = 19;
                    break;
                default:
                    break;
            }
            switch (dish.DishSize)
            {
                case "Small":
                    price *= 1;
                    break;
                case "Big":
                    price *= 2;
                    break;
                default:
                    break;
            }
            customer.Bill += price;
            Console.WriteLine("We received your order successfully.");
            Thread.Sleep(5000);
            Console.WriteLine("The dish is finished.May you have a good eating!");
        }

        internal void OrderAction(Customer customer, OrderEventArgs dish)
        {
            Console.WriteLine("What do you want to eat?");
            dish.DishName = Console.ReadLine();
            Console.WriteLine("What size do you want to eat?");
            dish.DishSize = Console.ReadLine();
            
        }

        internal void WelcomeAction(Customer customer, EventArgs e)//事件1的处理器
        {
            Console.WriteLine("Welcome to our restaurant!");
            Console.WriteLine("Please see the menu.The Big size is twice as the Small size:");
            Console.WriteLine("\tDishName\tPrice");
            Console.WriteLine("\tFriedFish\t10");
            Console.WriteLine("\tChickenSoup\t19");
        }
    }
    public class OrderEventArgs:EventArgs
    {
        public string DishName { get; set; }
        public string DishSize { get; set; }
    }
}

高亮一下事件声明方法:

    public delegate void OrderEventHandler(Customer customer, OrderEventArgs dish);
    //声明事件处理器的委托,注意命名方式

    public class Customer
    {
        private OrderEventHandler orderEventHandler;//声明事件处理器的委托字段
        public event OrderEventHandler Order
        {
            add
            {
                this.orderEventHandler += value;
            }
            remove
            {
                this.orderEventHandler -= value;
            }
        }
        protected void OrderDish()//事件触发内部逻辑,使用protected保护
        {
            ……
            OrderEventArgs dish = new OrderEventArgs();//声明事件参数,事件要通知的东西
            this.orderEventHandler.Invoke(this, dish);//触发事件
            ……
        }
        public void Action(Sever sever)//实现代码基本逻辑
        {
            this.Order += sever.OrderAction;//订阅
            this.OrderDish();//开始触发事件
        }

    }


    public class Sever
    {
        internal void OrderAction(Customer customer, OrderEventArgs dish)
        {
            ……
            
        }//事件处理器
    }

    public class OrderEventArgs:EventArgs//注意命名方式和父类
    {
        ……//事件参数的类
    }

事件有简要的声明,此时连委托也可以懒得命名。

namespace ConsoleAppPractice
{
    class Program
    {
        static void Main()
        {
            Console.ReadLine();

            Phone myPhone = new Phone();
            Me me = new Me();
            MessageEventArgs message = new MessageEventArgs();
            message.CreateMessage();
            myPhone.m = message;

            me.Sleeping();
            Thread.Sleep(1000);
            myPhone.Ring += me.Surprised;
            myPhone.Ringing();
            Thread.Sleep(3000);
            myPhone.GetMessage += me.Action;
            myPhone.Action();

            Console.ReadLine();
        }
    }
    public class Phone
    {
        public event EventHandler GetMessage;//使用自带的事件委托类型
        public event EventHandler Ring;
        public MessageEventArgs m;
        protected void OnGetMessage()
        {
            Console.WriteLine("My Phone:You have just received a message!");
            this.GetMessage.Invoke(this,this.m);
        }
        protected void OnRing()
        {
            Console.WriteLine("My Phone:dingdong~");
            Thread.Sleep(3000);
            this.Ring.Invoke(this, new EventArgs());//直接使用事件名。不过当然仅限于此
        }
        public void Ringing()
        {
            this.OnRing();
        }
        public void Action()
        {
            this.OnGetMessage();
        }
    }

    public class MessageEventArgs:EventArgs
    {
        public string senderName { get; set; }
        public string content { get; set; }
        public void CreateMessage()
        {
            this.senderName = "Mom";
            this.content = "What did you eat this morning?You should be healthy!";
        }
    }
    class Me
    {
        internal void Action(object sender, EventArgs e)
        {
            MessageEventArgs m = e as MessageEventArgs;//注意此处的类型转换
            Phone phone = sender as Phone;
            Console.WriteLine("Me:I received a message from {0}.",m.senderName);
            Console.WriteLine("Me:The content is:{0}",m.content);
        }
        public void Sleeping()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("Me:I am sleeping......");
                Thread.Sleep(1000);
            }
            
        }
        internal void Surprised(object sender, EventArgs e)
        {
            Console.WriteLine("Me:woc!?");
        }
    }
}

如果去掉event,成为完全的委托字段,会使其在类外也能被访问,安全性受损。故事件实质上是委托字段的包装器,集结了有逻辑关系的委托组,并将其封装,保护它的安全性,仅向外界暴露了添加/移除事件处理器的办法。

命名规范:

 

注:关于此处的protected:

        如果只想让其被自己的类及自己的类派生出的成员访问,则用protected。private不能被派生子类访问。

可以用如下的代码修改试试。

namespace ConsoleAppPractice
{
    class Program
    {
        static void Main()
        {
            B b = new B();
            b.haha();
        }
    }
    class A
    {
        protected void HAHA()
        {
            Console.WriteLine("Hh");
        }
    }
    class B : A
    {
        public void haha()
        {
            this.HAHA();
        }
    }
}

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值