C# 事件

概念

定义:事件英文解释为:a thing that happens,especially something important。中文就是“能发生的什么事情”。

角色:使对象或者类具备通知能力的成员
对象O拥有一个事件E:“当事件E发生的时候,O有能力通知别的对象”

使用:用于对象或类之间的动作协调与信息传递(消息推送)

原理:发生到相应中的5个动作——1、我有一个事件。2、一个人或一群人关心我这个事件。3、我的事件发生了。4、关心这个事件的人会被依次通知到。5、被通知到的人根据”事件参数“(事件参数=事件信息=事件数据=事件消息)对事件进行处理。

各种编程语言对事件的实现方法不同,Java中没有事件,也无委托。Java中事件由接口实现。

使用:事件多用于桌面、手机等开发的客户端编程

事件模型的五个组成部分

1.事件的拥有者(event source,事件的源头,是一个对象或一个类)

皮之不存毛将焉附?事件必须依附于类或对象存在

2.事件成员(event,事件本身,是对象或类的成员)

事件是一种能让对象或类具备通知能力的成员,从其拥有者角度来看,事件就是一个用于通知别人的工具,事件不会主动去通知别的对象或别的类,只有被其拥有者使事件发生后才会发生

3.事件响应者(event subscriber,事件的订阅者,是对象或类)

事件的响应者就是事件的订阅者,只有订阅了事件才能进行相应的响应,他们会使用自己的事件处理器来对事件进行应有的响应

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

事件的处理器是类或对象的成员,因为方法是类或对象中用于做事情的部分,而处理事件显然也要实现某些逻辑——因此,事件处理器也是一种方法成员,其是回调方法的原因:

事件处理器接收一个委托参数并根据这个参数来进行不同的操作,这些“不同的操作”指不同信息下对应的最终操作,因此是“根据情况选择使用哪个工具”的回调方法

5.事件订阅:将事件处理器与事件关联在一起,本质上是一种以委托类型为基础的“约定”

使用C#事件实例

实例一: “一个事件可以同时挂接多个事件处理器

	//每1秒屏幕上打印jump和sing
    class Program
    {
        static void Main(string[] args)
        {
            //timer 事件的拥有者
            Timer timer = new Timer();
            timer.Interval = 1000;
            //boy&girl 事件响应者
            Boy boy = new Boy();
            Girl girl = new Girl();
            //Elapsed 事件
            //Action 事件处理器
            //事件订阅
            timer.Elapsed += boy.Action;
            timer.Elapsed += girl.Action;

            timer.Start();
            Console.ReadLine();
        }
    }

    class Boy
    {
	//Action 事件处理器
        internal void Action(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine("Jump");
        }
    }

    class Girl
    {
	//Action 事件处理器
        internal void Action(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine("Sing");
        }
    }

 实例二
事件拥有者与事件响应者是完全不同的两个对象;
事件响应者用自己的方法(事件处理器)订阅事件,当事件发生时事件处理器就执行了。

		//当鼠标点击窗口时,窗口名称显示当前时间
		static void Main(string[] args)
        {
            //form 事件的拥有者
            Form form = new Form();
            //事件的响应者
            Controller controller = new Controller(form);
            form.ShowDialog();
        }

        class Controller
        {
            private Form form1;
            public Controller(Form form)
            {
                if(form!=null)
                {
                    this.form1 = form;
                    //Click 事件
                    //FormClicked 事件处理器
                    //事件订阅
                    this.form1.Click += this.FormClicked;
                }
            }
 			//FormClicked 事件处理器
            private void FormClicked(object sender, EventArgs e)
            {
                this.form1.Text = DateTime.Now.ToString();
            }
        }

 实例三
事件拥有者与事件响应者是同一个对象;
一个对象拿着自己的方法订阅和处理自己的事件
注意:派生==继承,即在原有的某个类的基础上扩展功能

	static void Main(string[] args)
        {
            //form 事件的拥有者&事件的响应者
            MyForm form = new MyForm();
            //Click 事件
            //FormClicked 事件处理器
            //事件订阅
            form.Click += form.FormClicked;
            form.ShowDialog();
        }

        class MyForm : Form
        {
            //FormClicked 事件处理器
            internal void FormClicked(object sender, EventArgs e)
            {
                this.Text = DateTime.Now.ToString();
            }
        }

实例四
事件拥有者是事件响应者的字段成员;
事件响应者用自己的方法订阅自己事件

在这里插入图片描述

//窗口里有一个按钮,点击这个按钮文本框里就显示“hello world”
	//非可视化编程,运行后要对控件位置进行调整
	static void Main(string[] args)
        {
            //form 事件的响应者
            MyForm form = new MyForm();
            form.ShowDialog();
        }

        class MyForm : Form
        {
            private TextBox textBox;
            //button 事件的拥有者
            private Button button;
            public MyForm()
            {
                //创建控件实例并且把控件添加到空间集合中
                this.button = new Button();
                this.textBox = new TextBox();
                this.Controls.Add(this.button);
                this.Controls.Add(this.textBox);
                //Click 事件
                //ButtonClicked 事件处理器
                //事件订阅
                this.button.Click += this.ButtonClicked;
            }

            private void ButtonClicked(object sender, EventArgs e)
            {
                this.textBox.Text = "Hello world!!!!!!!!!!!!!";
            }
        }

	//可视化编程
	//在代码中无法找到事件订阅,但是右击事件处理器查找“所有引用”中可以看到“事件订阅”已经自动生成
	public partial class Form1 : Form
    {
        //form 事件的响应者
        public Form1()
        {
            InitializeComponent();
        }

        //Click 事件
        //button 事件的拥有者
        //myButton_Click 事件处理器
        private void myButton_Click(object sender, EventArgs e)
        {
            textBox.Text = "Hello,World!";
        }

事件的声明

事件的本质

  • 委托字段的包装器,对委托字段的访问起限制作用
  • 事件对外界隐藏了委托实例的大部分功能,仅暴露添加或移除事件处理器的功能

用于声明事件的委托类型的命名约定

  • 用于声明Foo事件的委托,一般命名为FooEventHandler
  • FooEventHandler委托的参数一般有两个:
  • object类型,名字为sender,事件的拥有者
  • EventArgs类的派生类,类名一般为FooEventArgs,参数名为e,事件参数
  • 触发事件Foo的方法一般命名为OnFoo,访问级别一般为protected,不能为public,否则容易被误用。

事件的命名约定

  • 带有时态的动词/动词短语
  • 事件拥有者正在做什么事用进行时,事件拥有者完成了什么事用完成时。

有了委托字段属性,为什么还需要事件?
为了程序的逻辑更加有道理、更加安全

完整声明和简易声明

namespace ConsoleApp1
{
    //案例:餐厅服务员订阅和处理点菜
    //Customer 事件的拥有者
    //声明委托类型,为声明Order事件做准备,注意与声明委托类型字段区分
    //Order 声明点菜事件
    //Waiter 事件响应者
    //Action 事件处理器
    //事件订阅

    class Program
    {
        static void Main(string[] args)
        {
            Customer customer = new Customer();
            Waiter waiter = new Waiter();
            //Action 事件处理器
            //事件订阅
            customer.Order += waiter.Action;
            customer.Action();
            customer.PayTheBill();
        }
    }

    //传递事件信息的类
    public class OrderEventArgs:EventArgs
    {
        public string DishName { get; set; }
        public string Size { get; set; }
    }

    //声明委托类型,为声明Order事件做准备
    public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);

    //事件的拥有者
    public class Customer
    {
        //Order 声明事件   点菜事件
        //完整事件声明
        /*private OrderEventHandler orderEventHandler;
        public event OrderEventHandler Order
        {
            add
            {
                this.orderEventHandler += value;
            } 

            remove
            {
                this.orderEventHandler -= value;
            }
        }*/

        //简易声明
        public event OrderEventHandler Order;

        public double Bill { get; set; }
        public void PayTheBill()
        {
            Console.WriteLine("I will pay ${0}.",this.Bill);
        }

        public void WalkIn()
        {
            Console.WriteLine("walking");
        }

        public void SitDown()
        {
            Console.WriteLine("Sit down");
        }

        public void Think()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("thinking");
                Thread.Sleep(1000);
            }

            if (this.Order!=null)
            {
                OrderEventArgs e = new OrderEventArgs();
                e.DishName = "KongPao Chicken";
                e.Size = "large";
                this.Order.Invoke(this, e);
            }
        }

        public void Action()
        {
            Console.ReadLine();
            this.WalkIn();
            this.SitDown();
            this.Think();
        }
    }
    //Waiter 事件响应者
    public class Waiter
    {
        internal void Action(Customer customer,OrderEventArgs e)
        {
            //上菜加记账
            Console.WriteLine("I will serve you the dish-{0}",e.DishName);
            double price = 10;
            switch(e.Size)
            {
                case "small":
                    price = price * 0.5;
                    break;
                case "large":
                    price = price * 1.5;
                    break;
                default:
                    break;
            }

            customer.Bill += price;
        }
    }

}

事件与委托的关系

事件真的是"以特殊方式声明的委托字段/实例”吗?

  1.  不是。只是声明的时候"看起来像”(对比委托字段与事件的简化声明, field-like );
  2. 事件声明的时候使用了委托类型,简化声明造成事件看上去像一个委托的字段(实例) , 而event关键字则更像是一个修饰符这就是错觉的来源之一;
  3. 订阅事件的时候+=操作符后面可以是一个委托实例,这与委托实例的赋值方法语法相同,这也让事件看起来像是一个委托字段,这是错觉的又一来源;
  4. 事件的本质是委托字段的包装器。

为什么要使用委托类型来声明事件?

  1. 站在source的角度来看,是为了表明source能对外传递哪些消息;
  2. 站在subscriber的角度来看,它是一 种约定,是为了约束能够使用什么样签名的方法来处理(响应)事件;
  3. 委托类型的实例将用于存储(引用)事件处理器。

对比事件与属性

  1. 属性不是字段,很多时候属性是字段的包装器,这个包装器用来保护字段不被滥用;
  2. 事件不是委托字段,它是委托字段的包装器,这个包装器用来保护委托字段不被滥用;
  3. 包装器永远都不可能是被包装的东西。
     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值