C#事件的原理和用法整理

2022/4/9 14:19:09

本文主要是介绍C#事件的原理和用法整理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

事件是什么?

事件是能够让对象或类具备通知能力的成员。是一种类型成员(没有产品就没有发布,没有公司就没有上市)。它是用于对象和类之间的动作协调和信息传递的

 

事件模型如下:

“发生->响应”

5个动作——

  1. 我有一个事件
  2. 我关心这个事件
  3. 这个事件发生了
  4. 关心这个事件的人被通知到
  5. 被通知到的人根据拿到的事件信息对事件进行响应

 

5个部分——

  1. 事件的拥有者(event source 对象)
  2. 事件成员(event 成员) 事件是被动触发的,它需要通过事件拥有者进行触发
  3. 事件的响应者(eventsubscriber 对象)当事件发生时,哪些对象去响应
  4. 事件处理器(event handler 成员)响应者的方法成员
  5. 事件订阅     把事件处理器和事件关联在一起,本质上是一种以委托类型为基础的约定

 

从响应者角度来看,他们除了收到事件通知,还接收到经由事件发送过来的与事件本身相关的信息,称为“事件参数”(Event Args)

被通知的人根据事件参数对事件进行响应(处理事件),(处理事件)所做的事情,称为事件处理器(Event Handler)

 

因此我们可以说 事件的功能就是=通知+可选的事件模型

我们可以通过一个简单的代码例子来理解5个组成部分的关系。

查看代码
    class Program
    {
        static void Main(string[] args)
        {
            Timer timer = new Timer();//timer是事件拥有者
            timer.Interval = 1000;
            Boy boy = new Boy();//boy是事件响应者
            timer.Elapsed += boy.Action; //Elapsed是事件 +=是订阅
            timer.Start();
            Console.ReadKey();
        }
    }

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

我们用Boy作为事件响应者 用其事件处理器Action来订阅事件拥有者Timer类对象中自带的Elapsed事件

 

此外对于事件我们的使用情况通常有三种:

1、事件拥有者和响应者是两个相互独立的类,响应者类的处理器订阅拥有者类的事件

在这里插入图片描述

查看代码
namespace EventExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Form form = new Form();
            Controller controller = new Controller(form);
            form.ShowDialog();
        }
    }

    class Controller
    {
        private Form form;
        public Controller(Form form)
        {
            if (form != null)
            {
                this.form = form;
                this.form.Click += this.Clicked;
            }
        }

        private void Clicked(object sender, EventArgs e)
        {
            this.form.Text = DateTime.Now.ToString();
        }
    }
}

2、事件拥有者和响应者属于同一个类,这个类自己订阅自己的事件

在这里插入图片描述

namespace EventExample
{
    class Program
    {
        static void Main(string[] args)
        {
            MyForm myForm = new MyForm();
            myForm.Click += myForm.FormClicked;
            myForm.ShowDialog();
        }
    }

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

3、事件拥有者作为响应者的一个成员,或者是事件响应者作为拥有者的一个成员

在这里插入图片描述

查看代码
namespace EventExample
{
    class Program
    {
        static void Main(string[] args)
        {
            MyForm myForm = new MyForm();
            myForm.ShowDialog();
        }
    }

    class MyForm : Form
    {
        private TextBox TextBox;
        private Button button;
        public MyForm()
        {
            this.TextBox = new TextBox();
            this.button = new Button();
            this.Controls.Add(this.button);
            this.Controls.Add(this.TextBox);
            this.button.Click += this.ButtonClicked;
            this.button.Text = "Click";
            this.button.Top = 50;
        }

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

 

上面我们用的是C#帮我们写好的事件

那我们如何来自定义一个事件呢?C#自带事件的内部实现是什么样的呢?

 

还记得我们之前说的,事件是基于委托的吗。这个特性会在我们声明事件时进行一个体现

 

委托有以下作用:

类型兼容:委托对事件做了一个约束,规定了事件和事件处理器应当匹配

存储方法的引用:委托类型的实例将这组匹配信息保存了下来

 

因此我们说事件需要搭配一个委托。它通常是声明在事件拥有者类外的

// 步骤1,声明delegate  如果这个委托是为了约束某个事件而声明的委托 那么通常就将其命名为“事件名+EventHandler”
    public delegate void MyEventHandler(object sender, System.EventArgs e);

我们可以给个更加具体的例子,比如我们声明一个点餐的委托,我们想让Customer顾客作为事件拥有者,还要传递价格、大小等事件参数。那我们就可以这样声明

public delegate void OrderEventHandler(Customer customer, EventArgs e);

 

之后我们就可以声明我们的事件了。(记住事件是属于事件拥有者的,它需要写在事件拥有者类的内部)

有完整声明和简略声明两种形式

完整声明:

private OrderEventHandler OrderEventHandler;//声明委托类型字段。用于存储和引用事件处理器

public event OrderEventHandler Order//声明事件Order。用OrderEventHandler来约束事件
{
    add//事件处理器的添加器
    {
        this.OrderEventHandler += value;
    }

    remove//事件处理器的移除器
    {
        this.OrderEventHandler -= value;
    }
}

简略声明:

private event MyEventHandler myevent;

 

多种事件订阅格式

this.button3.Click += MyButton_Click;//第一种挂接事件方法
this.button3.Click += new EventHandler(this.MyButton_Click);//第二种
this.button3.Click += delegate (object sender, EventArgs e)//第三种 匿名方法
{
    this.MyTextBox.Text = "haha";
};
this.button3.Click += (sender,e) =>  //第四种 lambda表达式
{
    this.MyTextBox.Text = "hoho";
};

事件与委托的关系
事件真的是“以特殊方式声明的委托字段/实例吗”?
不是!只是声明的时候“看起来像”(对比委托字段的和事件的简化声明,field-like)


事件声明的时候使用了委托类型,简化声明造成事件看上去像一个委托的字段(实例),而event关键字则更像是一个修饰符——这就是错觉的来源之一
订阅事件的时候+=操作符后面可以是一个委托实例,这与委托实例的赋值方法语法相同,这也让事件看起来像是一个委托字段——这是错觉的又一来源
重申:事件的本质是加装在委托字段上的一个“蒙板”(mask),是个起掩蔽作用的包装器,这个用于阻挡非法操作的“蒙板”绝不是委托字段本身


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


对比事件和属性
属性不是字段——很多时候属性是字段的包装器,这个包装器用来保护字段不被滥用
事件不是委托字段——它是委托字段的包装器,这个包装器用于保护委托字段不被滥用
包装器永远都不可能是被包装的东西

下面给出一个参考实例

查看代码
namespace EventExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Customer customer = new Customer();
            Waiter waiter = new Waiter();

            customer.Order += waiter.Action;//挂接事件。waiter的Action订阅着customer的Order
            customer.Action();
            customer.PayTheBill();
        }
    }

    public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);//声明一个委托类型,专门用来声明事件,约束事件处理器


    public class OrderEventArgs : EventArgs//声明用来传递消息的类,派生自EventArgs
    {

        public string DishName { get; set; }
        public string Size { get; set; }
    }


    public class Customer //事件发起者
    {
        public event OrderEventHandler Order;//声明事件Order。用OrderEventHandler来约束事件
        
        public double Bill { get; set; }

        public void Walkin()
        {
            Console.WriteLine("Walk in the restaurant");
        }

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

        public void Think()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("Let me think......");
                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();
            Walkin();
            Sitdown();
            Think();
        }
        
        public void PayTheBill()
        {
            Console.WriteLine("I Will pay ${0}.",this.Bill);
        }
    }

    public class Waiter  //事件的响应者Waiter
    {
        public 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;
        }
    }
}


这篇关于C#事件的原理和用法整理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程