C#学习 - 事件 续

事件声明

完整声明

using System;namespace ConsoleApp1
{internal class Program{static void Main(string[] args){Customer customer = new Customer();Waiter waiter = new Waiter();customer.Order += waiter.Action;customer.Action();//这里也能直接写MyOrdercustomer.PayBill();}}//class前需加上public,否则访问级别低于OrderEventHandler会报错public class OrderEventArgs : EventArgs//继承于基类:EventArgs{   //传递事件信息,名字要以事件名+EventArgspublic string DishName { get; set; }//菜名public string Size {  get; set; }//份量}public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);//第一个参数:点菜人;第二个参数:点了什么/点了多少//委托是为了声明某个事件而准备的,需要用EventHandler作为后缀public class Customer//事件拥有者:顾客(类){   //class前需加上public,否则访问级别低于OrderEventHandler会报错private OrderEventHandler orderEventHandler;//委托字段,用来引用事件处理器public event OrderEventHandler Order//声明事件{add//事件处理器的添加器{this.orderEventHandler += value;//value == 传进来的EventHandler}remove//事件处理器的移除器{this.orderEventHandler -= value;}}public double Bill { get; set; }//账单(属性)public void PayBill(){Console.WriteLine("Total: ${0}", this.Bill);}public void MyOrder()//顾客点菜,触发事件{if(this.orderEventHandler != null){OrderEventArgs e = new OrderEventArgs();Console.WriteLine("You can order dish.");e.DishName = Console.ReadLine();//选择菜名Console.WriteLine("Enter the portion size of the dish.");e.Size = Console.ReadLine();//选择份量this.orderEventHandler.Invoke(this, e);}}public void Action()//触发MyOrder{Console.ReadLine();this.MyOrder();}}public class Waiter//事件的响应者:服务员(类){public void Action(Customer customer, OrderEventArgs e){Console.WriteLine("Serving your {0}",e.DishName);double price = 5;//菜的价格switch(e.Size){case "1":price = price * 1;break;case "2":price = price * 2; break;case "3":price = price * 4;break;default:break;}customer.Bill += price;}}
}

简略声明

字段式声明(field - like)

private OrderEventHandler orderEventHandler;
public event OrderEventHandler Order
{add{this.orderEventHandler += value;}remove{this.orderEventHandler -= value;}
}
//将上段代码删除,修改为
public event OrderEventHandler Order;
//同时,将下段代码中的orderEventHandler修改为事件名Order
public void MyOrder()
{if(this.orderEventHandler/*此处*/ != null){OrderEventArgs e = new OrderEventArgs();Console.WriteLine("You can order dish.");e.DishName = Console.ReadLine();Console.WriteLine("Enter the portion size of the dish.");e.Size = Console.ReadLine();this.orderEventHandler/*此处*/.Invoke(this, e);}
}

简略声明中的语法与完整声明中有不一致处。在完整声明中,不能用Order(事件名)替代orderEventHandler(事件处理器),事件名只能处于 += 或 -=操作符左边;而简略声明中没有声明委托字段orderEventHandler,只能用Order替代(在简略声明中,编译器暗地里声明了一个名为Order的字段)

事件的必要性

简略声明事件与直接使用委托类似(只在声明时多了一个event),但事件能让程序逻辑以及对象之间的关系更加有好,让程序更加安全

//简略声明中删去event
static void Main(string[] args)
{Customer customer = new Customer();Waiter waiter = new Waiter();customer.Order += waiter.Action;customer.Action();OrderEventArgs e1 = new OrderEventArgs();e1.DishName = "Fried rice with egg";e1.Size = "1";OrderEventArgs e2 = new OrderEventArgs();e2.DishName = "Coke";e2.Size = "1";Customer c = new Customer();c.Order += waiter.Action;c.Order.Invoke(customer, e1);//事件不能用“.”操作符,而委托可以c.Order.Invoke(customer, e2);customer.PayBill();//customer被迫付了一份蛋炒饭和一份可乐的钱
}

事件本质

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

  • 包装器对委托字段的访问起限制作用
  • 封装(encapsulation)的一个重要功能就是隐藏
  • 事件对外界隐藏了委托实例的大部分功能,只暴露了添加&移除事件处理器的功能

事件类委托命名规则

声明事件A的委托,命名为AEventHandler(事件名 + EventHandler)。有个通用委托:public delegate void EventHandler(object sender, EventArgs e);
AEventHandler委托参数一般有两个

  • object类型:参数名为sender,事件拥有者
  • EventArgs类的派生类:类名一般为AEventArgs,参数名为e,事件参数
  • 委托的参数列表可以看作是事件发生后发送给事件响应者的“事件消息”
    触发A事件的方法一般命名为OnA,访问级别为protected而非public
    之前的代码中没有使用OnOrder,若要遵守规则,则需修改:
public void MyOrder()
{if(this.orderEventHandler != null){OrderEventArgs e = new OrderEventArgs();Console.WriteLine("You can order dish.");e.DishName = Console.ReadLine();//选择菜名Console.WriteLine("Enter the portion size of the dish.");e.Size = Console.ReadLine();//选择份量this.orderEventHandler.Invoke(this, e);}
}
//将事件声明中的上段代码修改为下面的
public void MyOrder()
{Console.WriteLine("You can order dish.");string dish = Console.ReadLine();Console.WriteLine("Enter the portion size of the dish.");string size = Console.ReadLine();this.OnOrder(dish, size);
}
protected void OnOrder(string dish, string size)
{if(this.orderEventHandler != null){OrderEventArgs e = new OrderEventArgs();e.DishName = dish;e.Size = size;this.orderEventHandler.Invoke(this, e);//事件拥有者触发事件,传this(this就是事件拥有者)}
}
//简略声明就把orderEventHandler改为Order

事件命名

带有时态的动词或动词短语
命名时要使用对应的时态

事件与委托的关系

事件不是以特殊方式声明的委托字段或实例:

  • 事件进行简略声明时很像委托字段
  • 订阅事件时 += 操作符后面可以是一个委托实例,与委托实例的赋值方法语法相同,让事件看起来像个字段customer.Order += new EventHandler(waiter.Action);

委托类型来声明字段:

  • 在事件拥有者(source)来看,是为了表明能向外传递哪些信息
  • 在事件响应者(subscriber)来看,是为了约束能够使用什么样签名的方法来处理事件
  • 委托类型的实例将用于存储(引用)事件处理器

事件与属性:

  • 属性不是字段,很多时候属性是字段的包装器,用来保护字段不被滥用
  • 时间不是委托字段,是委托字段的包装器,用来保护委托字段不被滥用
  • 包装器不能被另一个包装器包装

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

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

相关文章

python爬虫之创建属于自己的ip代理池

在后续需求数据量比较大的情况下,自建一个ip代理池可以帮助我们获得更多的数据。 下面我来介绍一下整个过程 1.找到目标代理网站 https://www.dailiservers.com/go/webshare https://proxyscrape.com/ https://spys.one/ https://free-proxy-list.net/ http://fr…

找不到DNS地址的解决方案

找不到DNS地址的解决方案 第一种解决方案:刷新DNS缓存第二种解决方案: 配置Internet协议版本4(TCP/IPv4)配置IP地址配置DNS地址 如何查看本机IPv4地址、子网掩码与默认网关 第一种解决方案:刷新DNS缓存 WINR输入cmd回…

基于SSH三大框架的员工管理系统

基于SSH三大框架的员工管理系统 摘要 本系统为本人学习SSH三大框架时所做的整合实例,系统角色包括普通用户和管理员两种,首页有管理员登录入口链接。系统功能主要包括管理员对用户的基本增、删、改、查和分页显示用户信息等。 系统环境 本系统使用ec…

【C++练级之路】【Lv.1】C++,启动!(命名空间,缺省参数,函数重载,引用,内联函数,auto,范围for,nullptr)

目录 引言入门须知一、命名空间1.1 作用域限定符1.2 命名空间的意义1.3 命名空间的定义1.4 命名空间的使用 二、C输入&输出2.1 cout输出2.2 cin输入2.3 std命名空间的使用惯例 三、缺省参数3.1 缺省参数概念3.2 缺省参数分类 四、函数重载4.1 函数重载概念4.2 函数重载分类…

BUUCTF 间谍启示录 1

BUUCTF:https://buuoj.cn/challenges 题目描述: 在城际公路的小道上,罪犯G正在被警方追赶。警官X眼看他正要逃脱,于是不得已开枪击中了罪犯G。罪犯G情急之下将一个物体抛到了前方湍急的河流中,便头一歪突然倒地。警官X接近一看&…

公平锁和非公平锁以及他们的实现原理是什么

文章目录 什么是非公平锁和公平锁呢?我们来看看acquire(1)的源码如下:这里的判断条件主要做两件事:在tryAcquire()方法中,主要是做了以下几件事:公平锁的tryAcquire(),实现的原理图如下:我们来看…

ORA-00257: archiver error. Connect internal only, until freed 的解决方法

归档文件存储空间不足,导致出现该问题。 当我们将数据库的模式修改为归档模式的时候,如果没有指定归档目录,默认的归档文件就会放到Flash Recovery Area的目录,但是这个目录是有大小限制的,如果超过了这个大小&#x…

C#基础学习--命名空间和程序集

引用其他程序集 编译器接受源代码文件并生成一个名为程序集的输出文件。 在许多项目中,会想使用来自其他程序集的类或类型。这些程序集可能来自BCL或第三方供应商,或者自己创建的。这些程序集称为类库,而且它们的程序集文件的名称通常以dll…

微信小程序组件与插件有啥区别?怎么用?

目录 一、微信小程序介绍 二、微信小程序组件 三、微信小程序插件 四、微信小程序组件与插件有啥区别 一、微信小程序介绍 微信小程序是一种基于微信平台的应用程序,它可以在微信客户端内直接运行,无需下载和安装。微信小程序具有轻量、便捷、跨平台…

对比ProtoBuf和JSON的序列化和反序列化能力

1.序列化能力对比验证 在这里让我们分别使用PB与JSON的序列化与反序列化能力,对值完全相同的一份结构化数据进行不同次数的性能测试。 为了可读性,下面这一份文本使用JSON格式展示了需要被进行测试的结构化数据内容: {"age" : 20,"name…

线程安全的问题以及解决方案

线程安全 线程安全的定义 线程安全:某个代码无论是在单线程上运行还是在多线程上运行,都不会产生bug. 线程不安全:单线程上运行正常,多线程上运行会产生bug. 观察线程不安全 看看下面的代码: public class ThreadTest1 {public static int count 0;public static void main…

数据结构和算法-树与二叉树的存储结构以及树和二叉树和森林的遍历

文章目录 二叉树的存储结构二叉树的顺序存储二叉树的链式存储小结 二叉树的先中后序遍历例题小结 二叉树的层次遍历小结 由遍历序列构造二叉树一个遍历序列即使给定了前中后序,也不能确定该二叉树的形态可以确定的序列组合前序中序后序中序层序中序 小结若前序&…

算力基础设施领域国家标准发布

2023 年 11 月 27 日,国家标准 GB/T 43331-2023《互联网数据中心(IDC)技术和分级要求》正式发布。这一国家标准由中国信息通信研究院(简称“中国信通院”)联合多家企事业单位编制,旨在满足当前国家算力基础…

强化学习(一)——基本概念及DQN

1 基本概念 智能体 agent ,做动作的主体,(大模型中的AI agent) 环境 environment:与智能体交互的对象 状态 state ;当前所处状态,如围棋棋局 动作 action:执行的动作,…

C#——Delegate(委托)与Event(事件)

C#——Delegate(委托)与Event(事件) 前言一、Delegate(委托)1.是什么?2.怎么用?Example 1:无输入无返回值Example 2:有输入Example 3:有返回值Exa…

子集II(回溯+去重)

题目描述 给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。 解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。 样例输入 示例 1: 输入&#xff1…

【C#】接口定义和使用知多少

给自己一个目标,然后坚持一段时间,总会有收获和感悟! 最近在封装和参考sdk时,看到一个不错的写法,并且打破自己对接口和实现类固定的观念,这也充分说明自己理解掌握的知识点还不够深。 目录 前言一、什么是…

Kubernetes(K8s)_16_CSI

Kubernetes(K8s)_16_CSI CSICSI实现CSI接口CSI插件 CSI CSI(Container Storage Interface): 实现容器存储的规范 本质: Dynamic Provisioning、Attach/Detach、Mount/Unmount等功能的抽象CSI功能通过3个gRPC暴露服务: IdentityServer、ControllerServe…

C++二维数组名到底代表个啥

题目先导 int a[3][4]; 则对数组元素a[i][j]正确的引用是*(*(ai)j)先翻译一下这个*(*(ai)j),即a后移i解引用,再后移j再解引用,这么看来a就应该是个二维数组,第一层存储行向量,一次解引用获得行向量的地址,…

LLM推理部署(三):一个强大的LLM生态系统GPT4All

GPT4All,这是一个开放源代码的软件生态系,它让每一个人都可以在常规硬件上训练并运行强大且个性化的大型语言模型(LLM)。Nomic AI是此开源生态系的守护者,他们致力于监控所有贡献,以确保质量、安全和可持续…