C#入门 020 事件(类型成员)

news2024/12/23 13:48:45

初步了解事件

定义:单词Event,译为“事件”

  • 《牛津词典》中的解释是“a thing that happens, especially something important
  • 通顺的解释就是“能够发生的什么事情”

角色:使对象或类具备通知能力的成员

  • 事件(event)是一种使对象或类能够提供通知的成员
  • 对象O拥有一个事件E"想表达的思想是:当事件E发生的时候,O有能力通知别的对象

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

原理:事件模型(event model)中的两个"5

"发生>响应”中的5个部分--闹钟响了你起床、孩子饿了你做饭.…这里隐含着“订阅”关系"

发生→响应"中的5个动作--

  • (1)我有一个事件→
  • (2)一个人或者一群人关心我的这个事件>
  • (3)我的这个事件发生了→
  • (4)关心这个事件的人会被依次通知到>
  • (5)被通知到的人根据拿到的事件信息(又称“事件数据”、“事件参数”、“通知”)对事件进行响应(又称“处理事件”)

提示

  • 事件多用于桌面、手机等开发的客户端编程,因为这些程序经常是用户通过事件来“驱动”的务种编程语言对这个机制的实现方法不尽相同
  • Java语言里没有事件这种成员,也没有委托这种数据类型。Java的“事件”是使用接口来实现的MVC、MVP、MVVM等模式,是事件模式更高级、更有效的“玩法
  • 日常开发的时候,使用已有事件的机会比较多,自己声明事件的机会比较少,所以先学使用

事件的应用

实例演示

派生(继承)与扩展(extends)。

事件模型的五个组成部分

  1. 事件的拥有者(event source,对象) 主体、消息的发送者 ,
    1. 事件不会主动发生 , 是被拥有者内部逻辑触发之后才能发生
    2. 谁调用事件谁就是事件的拥有者
  2. 事件成员(event,成员) 事件本身
  3. 事件的响应者(event subscriber,对象)
  4. 事件处理器(event handler,成员)一本质上是一个回调方法4.
  5. 事件订阅--把事件处理器与事件关联在一起,本质上是一种以委托类型为基础的“约定”

注意

  • 事件处理器是方法成员
  • 挂接事件处理器的时候,可以使用委托实例,也可以直接使用方法名,这是个“语法糖”
  • 事件处理器对事件的订阅不是随意的,匹配与否由声明事件时所使用的委托类型来检测
  • 事件可以同步调用也可以异步调用

深入理解事件

两星

  • 一个对象拿着自己的方法去订阅和处理自己的事件
  • 事件的拥有者和事件的响应者是同一个对象

C#自带的类(比如Form)都不能在添加事件

三星

  • 事件拥有者是事件响应者的一个字段成员
  • 事件响应者用自己的方法订阅着自己的字段成员的某个事件

注意

  • 事件处理器是方法成员
  • 挂接事件处理器的时候,可以使用委托实例,也可以直接使用方法名,这是个“语法糖”
  • 事件处理器对事件的订阅不是随意的,匹配与否由声明事件时所使用的委托类型来检测
  • 事件可以同步调用也可以异步调用

事件的声明

完整声明

  • 事件需要委托类型来做一个约束,这个约束规定了事件能发送什么消息给响应者,也规定了事件的响应者能收到什么类型的消息
  • 当事件响应者向事件拥有者提供了能够匹配这个事件的事件处理器之后,需要把事件处理器保存或者记录下来。能够记录或者说引用方法的任务,只有委托类型的实例能够做到。

简略声明(字段式声明,field-like )

声明示例

首先,我们定义了一个OrderEventArgs类,它继承自.NET的EventArgs类。这个类用于传递订单的信息(菜名和大小)。

接着,我们定义了OrderEventHandler委托类型,它接受两个参数:一个Customer对象和一个OrderEventArgs对象。

然后是Customer类,它有一个名为Order的事件,该事件就是之前定义的OrderEventHandler类型。当顾客点餐时,就会触发这个事件。Customer类还有几个方法模拟顾客的行为,如进入餐厅、坐下、思考点什么菜,以及最后付账。

Think方法中,顾客思考要吃什么,并通过orderEventHandler调用事件处理程序,这里实际上是通知服务员(Waiter类)他们点了什么菜。

Waiter类有一个内部方法Action,这个方法作为事件处理程序被注册到Customer的Order事件上。当顾客点餐后,服务员根据订单计算价格并更新顾客的账单。

最后,在Program类的Main方法里,创建了一个顾客实例和一个服务员实例,并将服务员的Action方法注册为顾客Order事件的处理程序。然后模拟顾客进店、坐下、思考并点餐的过程,最后付账。

总结来说,这段代码演示了如何使用事件来实现对象之间的通信。当一个对象(这里是Customer)发生了一些事情(例如点餐),它可以通知其他对象(这里是Waiter)来执行相应的操作(如记录订单和计算费用)。这是一种解耦的设计模式,使得对象之间不需要直接相互引用就可以进行通信。

 

using System;
using System.Threading;

namespace 刘铁猛
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // 创建顾客实例
            Customer customer = new Customer();
            // 创建服务员实例
            Waiter waiter = new Waiter();
            // 注册服务员的动作作为顾客点餐事件的处理程序
            customer.Order += waiter.Action;
            // 顾客行动开始
            customer.Action();
            // 顾客付账
            customer.PayTheBill();

            Console.ReadLine(); // 暂停程序,等待用户输入
        }
    }

    // 定义订单事件参数类
    public class OrderEventArgs : EventArgs 
    {
        public string DishName { get; set; } // 菜名
        public string Size { get; set; }     // 大小
    }

    // 定义委托类型,用于处理订单事件
    public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);

    // 顾客类
    public class Customer
    {   //pbulic event  OrderEventHandler Order;简略声明事件 示例
        private OrderEventHandler orderEventHandler; // 存储事件处理程序的字段

        // 定义事件Order,允许外部对象注册事件处理程序
        public event OrderEventHandler Order
        {
            add
            {
                this.orderEventHandler += value;
            }
            remove
            {
                this.orderEventHandler -= value;
            }
        }

        public double Bill { get; set; } // 顾客的账单金额

        // 付账方法
        public void PayTheBill()
        {
            Console.WriteLine("i will pay ${0}", this.Bill); // 输出账单信息
        }

        // 进入餐厅方法
        public void walkIn()
        {
            Console.WriteLine("Walk into the restaurant"); // 输出进入餐厅的信息
        }

        // 坐下方法
        public void sitDown()
        {
            Console.WriteLine("Sit down......"); // 输出坐下信息
        }

        // 思考方法
        public void Think()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("Let me ...."); // 输出思考的信息
                Thread.Sleep(1000); // 模拟思考时间,暂停一秒
            }
            if (this.orderEventHandler != null) // 如果有事件处理程序注册
            {
                OrderEventArgs e = new OrderEventArgs(); // 创建订单事件参数实例
                e.DishName = "辣椒炒肉"; // 设置菜名
                e.Size = "large"; // 设置大小
                this.orderEventHandler.Invoke(this, e); // 触发事件,通知服务员
            } //简略声明
            /*if (this.orderEventHandler != null) // 如果有事件处理程序注册
            {
                OrderEventArgs e = new OrderEventArgs(); // 创建订单事件参数实例
                e.DishName = "辣椒炒肉"; // 设置菜名
                e.Size = "large"; // 设置大小
                this.orderEventHandler.Invoke(this, e); // 触发事件,通知服务员
            }*/
        }

        // 主动作方法
        public void Action()
        {
            Console.ReadLine(); // 暂停,等待用户确认
            this.walkIn();      // 顾客进入餐厅
            this.sitDown();     // 顾客坐下
            this.Think();       // 顾客思考点什么菜
        }
    }

    // 服务员类
    public class Waiter
    {
        // 处理顾客点餐的方法
        internal void Action(Customer customer, OrderEventArgs e)
        {
            Console.WriteLine("I will 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; // 更新顾客账单
        }
    }
}

有了委托字段/属性,为什么还需要事件 ?

  • 为了程序的逻辑更加“有道理”、更加安全,谨防”借刀杀人”

所以事件的本质是委托字段的一个包装器

  • 这个包装器对委托字段的访问起限制作用,相当于一个“蒙板”
  • 封装(encapsulation)的一个重要功能就是隐藏
  • 事件对外界隐藏了委托实例的大部分功能,仅暴露添加/移除事件处理器的功能
  • 添加/移除事件处理器的时候可以直接使用方法名,这是委托实例所不具备的功能

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

  • 用于声明Foo事件的委托,一般命名为FooEventHandler(除非是一个非常通用的事件约束)
    • 凡是传递数据的类,都从EventArge类派生出来
  • FooEventHandler委托的参数一般有两个(由Win32 API演化而来,历史悠久)
    • 第一个是object类型,名字为sender,实际上就是事件的拥有者、事件的source。
    • 第二个是EventArgs类的派生类,类名一般为FooEventArgs,参数名为e。也就是前面讲过的事件参数
    • 虽然没有官方的说法,但我们可以把委托的参数列表看做是事件发生后发送给事件响应者的“事件消息
  • 触发Foo事件的方法一般命名为OnFoo,即“因何引发”、“事出有因”
    • 访问级别为protected,不能为public,不然又成了可以“借刀杀人”了

事件的命名约定

带有时态的动词或者动词短语

事件拥有者”正在做”什么事情,用进行时;事件拥有者“做完了”什么事情,用完成时

问题辨析

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2238577.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单链表算法题(数据结构)

1. 反转链表 https://leetcode.cn/problems/reverse-linked-list/description/ 题目&#xff1a; 看到这个题目的时候我们怎么去想呢&#xff1f;如果我们反应快的话&#xff0c;应该可以想到我们可以从1遍历到5然后依次头插&#xff0c;但是其实我们还有更好的办法&#xff…

Python 如何通过 cron 或 schedule 实现爬虫的自动定时运行

Python 如何通过 cron 或 schedule 实现爬虫的自动定时运行 自动定时运行爬虫是很多数据采集项目的基本需求。例如&#xff0c;每天采集一次新闻数据&#xff0c;或每小时更新股票行情数据等。通过 Python 实现定时任务&#xff0c;可以保证数据采集的高效和持续性。本文将带大…

初学mongoDB

MongoDB 是一个开源的 NoSQL 数据库&#xff0c;由 C 语言编写。它与传统的关系型数据库不同&#xff0c;MongoDB 使用的是一种基于文档的存储模型&#xff0c;不需要定义固定的表结构&#xff0c;可以灵活地存储和管理大量的非结构化数据。下面是 MongoDB 的一些核心特性&…

Ubuntu 的 ROS 操作系统turtlebot3环境搭建

引言 本文介绍如何在Ubuntu系统中为TurtleBot3配置ROS环境&#xff0c;包括安装和配置ROS Noetic的步骤&#xff0c;为PC端控制TurtleBot3提供操作指南。 安装和配置的过程分为PC设置、系统安装、依赖安装等部分&#xff0c;并在最后进行网络配置&#xff0c;确保PC端能够顺利…

图像增强的100种方法

文章目录 什么是图像增强 &#xff1f;一、亮度和对比度调整1.1、线性方法1.1.1、灰度反转&#xff08;Gray Inversion&#xff09;1.1.2、对比度拉伸&#xff08;Contrast Stretching&#xff09;1.1.3、对比度和亮度增强&#xff08;Contrast and Brightness&#xff09; 1.2…

Android Kotlin Flow 冷流 热流

在 Android 开发中&#xff0c;Flow 是 Kotlin 协程库的一部分&#xff0c;用于处理异步数据流的一个组件。本质上&#xff0c;Flow 是一个能够异步生产多个值的数据流&#xff0c;与 suspend 函数返回单个值的模式相对应。Flow 更类似于 RxJava 中的 Observable&#xff0c;但…

Web服务器nginx实验2修改端口、默认目录、默认文件访问web页面

修改默认目录、默认文件&#xff1a; 创建配置文件&#xff1a; 里面写&#xff1a;&#xff08;访问的位置是/haha目录里面的haha.html&#xff09; 把haha里面的index.html改名为haha.html&#xff1a; 重启服务&#xff1a; 关闭防火墙、改宽松模式&#xff1a; 用Windows访…

Maven最佳实践

文章目录 1.摘要 本文主要介绍Maven使用&#xff0c;作为Maven使用手册来记录。 2.介绍 Maven是项目管理工具&#xff0c;将项目开发和管理过程抽象成一个项目对象模型&#xff0c;使用pom.xml 文件进行依赖管理和项目构建。 Maven 中pom.xml 是根据坐标信息来定位资源的位置&a…

el-table 纵向垂直表头处理

项目中表格展示会遇到需要纵向垂直表头情况&#xff0c;下面&#xff0c;我们基于el-table组件来实现这种表格。 以下是这次需要用到的数据表格&#xff0c;已知左侧违章名称是固定的&#xff0c;而月份是不固定的&#xff0c;在后端返回数据格式已确定的情况下&#xff0c;需…

Android OpenGL ES详解——纹理:纹理过滤GL_NEAREST和GL_LINEAR的区别

目录 一、概念 1、纹理过滤 2、邻近过滤 3、线性过滤 二、邻近过滤和线性过滤的区别 三、源码下载 一、概念 1、纹理过滤 当纹理被应用到三维物体上时&#xff0c;随着物体表面的形状和相机视角的变化&#xff0c;会导致纹理在渲染过程中出现一些问题&#xff0c;如锯齿…

超市11-12月生鲜重点商品配置

11月份&#xff1a;应季商品很多,特别是与季节相对应的蔬菜大量上市。宜推荐对预防感冒等相应的特殊食谱,推荐对于常外出的人方便又省事、省时的食谱&#xff0c;推荐多种花样的火锅&#xff0c;推荐便于保存的应季食品原料。 生活特性&#xff1a;大众食谱宜以炖菜、红焖、火锅…

c++设计模式demo

模式设计原则 依赖倒置原则 ⾼层模块不应该依赖低层模块&#xff0c;⼆者都应该依赖抽象 &#xff1b; 抽象不应该依赖具体实现&#xff0c;具体实现应该依赖于抽象&#xff1b; ⾃动驾驶系统公司是⾼层&#xff0c;汽⻋⽣产⼚商为低层&#xff0c;它们不应该互相依赖&#x…

【网络面试篇】其他面试题——Cookie、Session、DNS、CDN、SSL/TLS、加密概念

目录 一、HTTP 相关问题 1. Cookie 和 Session 是什么&#xff1f; &#xff08;1&#xff09;Cookie &#xff08;2&#xff09;Session 2. Cookie 的工作原理&#xff1f; 3. Session 的工作原理&#xff1f; 4. Cookie 和 Session 有什么区别&#xff1f; 二、其他问…

软件测试第二篇软件测试技术

第五章单元测试和集成测试的技术 单元静态测试主要由开发人员完成。 标准&#xff1a;规定什么能做&#xff0c;什么不能做。 规范&#xff1a;建议你要怎么做。 5.1.2 代码评审 代码评审是一种发现代码缺陷的另一种测试方法。 代码审查的最佳实践&#xff1a; 创建代码审…

QT中 update()函数无法实时调用 paintEvent

QT中 update()函数无法实时调用 paintEvent&#xff01; 在QT中&#xff0c;update()函数用于标记一个窗口区域为“需要重绘”。当调用update()后&#xff0c;QT会在合适的时候调用paintEvent()来重绘这个区域。然而&#xff0c;update()不会立即调用paintEvent()&#xff0c;…

SDL事件相关

文章目录 事件相关的函数和数据结构用户自定义事件代码相关&#xff1a; 事件相关的函数和数据结构 SDL_WaitEvent :等待一个事件SDL_PushEvent 发送一个事件SDL_PumpEvents(): 将硬件设备产生的时间放入事件队列 &#xff0c;用于读取事件&#xff0c;在调用该函数之前&#…

优化时钟网络之时钟抖动

Note&#xff1a;文章内容以Xilinx 7系列FPGA进行讲解 1、什么是时钟抖动 时钟抖动就是时钟周期之间出现的偏差。比如一个时钟周期为10ns的时钟&#xff0c;理想情况下&#xff0c;其上升沿会出现在0ns&#xff0c;10ns&#xff0c;20ns时刻&#xff0c;假设某个上升沿出现的时…

达梦8-达梦数据实时同步软件(DMHS)配置-Oracle-DM8

1、安装环境 源端目的端IP地址192.168.6.111192.168.6.110系统版本Red Hat 6.4Kylin v10数据库版本Oracle11g达梦 v8系统用户Oracledmdba字符集MERICAN_AMERICA.AL32UTF8UTF-8端口15215236实例名PRODDMSERVER数据库软件目录/u01/app/oracle/opt/dmdbmsDMHS安装目录/u01/dmhs/o…

AI基础知识

目录 1.激活函数:one: 激活函数的作用:two: sigmoid函数:three: tanh函数:four: ReLu:five: Leaky ReLU 2.Softmax函数3.优化器:one: 优化器的作用:two: BGD&#xff08;批梯度下降&#xff09;:three: SGD&#xff08;随机梯度下降&#xff09;:four: MBGD&#xff08;Mini Ba…

【论文阅读】Learning dynamic alignment via meta-filter for few-shot learning

通过元滤波器学习动态对齐以实现小样本学习 引用&#xff1a;Xu C, Fu Y, Liu C, et al. Learning dynamic alignment via meta-filter for few-shot learning[C]//Proceedings of the IEEE/CVF conference on computer vision and pattern recognition. 2021: 5182-5191. 论文…