【C#语言入门】18. 事件详解(下)

【C#语言入门】18. 事件详解(下)

三、事件的声明

  • 事件的声明
    • 完整声明
    • 简略声明(字段式声明。field-like)

完整声明

class EventExample1
{static void Main(string[] args){Customer customer = new Customer();Waiter waiter = new Waiter();customer.Order += waiter.Action;customer.Action();customer.PayTheBill();}}public class OrderEventArgs
{public string DishName { get; set; }public string Size {  get; set; }
}public delegate void OrderEventHandler(Customer cumtomer, OrderEventArgs e);public class Customer
{private OrderEventHandler orderEventHandler;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("Emm...Let me think..");}if(this.orderEventHandler != null){OrderEventArgs e = new OrderEventArgs();e.DishName = "Kongpao Chicken";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 serve you the dish - {0}", e.DishName);double price = 10;switch (e.Size){case "small":price *= 0.5;break;case "large":price *= 1.5;break;default:break;}customer.Bill += price;}
}

简易声明,会有误解,事件名取代了字段名

public class Customer
{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("Walk into the restaurant.");}public void SitDown(){Console.WriteLine("Sit down.");}public void Think(){for (int i = 0;i < 5;i++){Console.WriteLine("Emm...Let me think..");}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();}
}
  • 有了委托字段/属性,为什么还需要事件
    • 为了程序的逻辑更加“有道理”、更加安全,谨防“借刀杀人”,事件的触发必须由事件自己来做。
class EventExample1
{static void Main(string[] args){Console.ReadLine();Customer customer = new Customer();Waiter waiter = new Waiter();customer.Order += waiter.Action;//customer.Action();OrderEventArgs e1 = new OrderEventArgs();e1.DishName = "BigggDinner";e1.Size = "large";OrderEventArgs e2 = new OrderEventArgs();e2.DishName = "Beer";e2.Size = "large";Customer badGuy = new Customer();badGuy.Order += waiter.Action;badGuy.Order.Invoke(customer,e1);badGuy.Order.Invoke(customer,e2);customer.PayTheBill();}}public class OrderEventArgs
{public string DishName { get; set; }public string Size {  get; set; }
}public delegate void OrderEventHandler(Customer cumtomer, OrderEventArgs e);public class Customer
{public OrderEventHandler Order;//没有了Event,变成了字段,谁都可以访问它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("Emm...Let me think..");}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();}
}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 *= 0.5;break;case "large":price *= 1.5;break;default:break;}customer.Bill += price;}
}
  • 所以事件的本质是委托字段的一个包装器
    • 这个包装器对委托字段的访问起到限制作用,相当于一个蒙版
    • 封装(encapsulation)的一个重要功能就是隐藏
    • 事件对外界隐藏了委托实例的大部分功能,仅暴露添加/移除事件处理器的功能
  • 用于声明事件的委托类型的命名约定
    • 用于声明Foo事件的委托,一般命名为FooEventHandler(除非是一个非常通用的事件约束)
    • FooEventHandler委托的参数一般有两个
      • 第一个是object类型,名字为sender,实际上就是事件的拥有者,事件的source
      • 第二个是EventArgs类的派生类,类名一般为FooEventArgs,参数名为e。也就是前面讲过的事件参数
      • 虽然没有官方的说法,但我们可以把委托的参数列表看做是事件发生后送给事件响应者的“事件消息”
    • 触发Foo事件的方法一般命名为OnFoo,即”因何引发“、”事出有因“
      • 访问级别为protected,不能为public,不然又可以“借刀杀人”
  • 事件的命名约定
    • 带有时态的动词或者动词短语
    • 事件拥有者“正在做”什么事情,用进行时;事件拥有者“做完了”什么事情,用完成时
public void Think()
{for (int i = 0;i < 5;i++){Console.WriteLine("Emm...Let me think..");}this.OnOrder("Kongpao Chicken", "large");}protected void OnOrder(string dishName, string size)//protected保护了这个方法,不让外界使用
{if (this.Order != null){OrderEventArgs e = new OrderEventArgs();e.DishName = dishName;e.Size = size;this.Order.Invoke(this, e);}
}
//优化,确保一个方法只做一件事情

四、事件与委托的关系

  • 事件真的是“以特殊方式声明的委托字段/实例”吗?
    • 错误的。只是声明的时候“看起来像”(对比委托字段与事件的简化声明,field-like)
    • 事件声明的时候使用了委托类型,简化声明造成时间看上去像一个委托的字段(实例),而event关键字则更像是一个修饰符——这就是错觉的来源之一
    • 订阅事件的时候 += 操作符后面可以是一个委托实例,这与委托实例的赋值方法语法相同,这也让事件看起来像是一个委托字段——这是错觉的又一来源
    • 重申:事件的本质是加装在委托字段上的一个“蒙版”,是个起掩蔽作用的包装器。这个用于阻挡非法操作的蒙版绝不是委托字段本身
  • 为什么要使用委托类型来声明事件?
    • 站在source的角度来看,是为了表明source能对外传递哪些消息
    • 站在subscriber的角度来看,它是一种约定,是为了约束能够使用什么样签名的方法来处理(响应)事件
    • 委托类型的实例将用于存储(引用)事件处理器
  • 对比事件和属性
    • 属性不是字段——很多时候属性是字段的包装器,这个包装器用来保护字段不被滥用
    • 事件不是委托字段——它是委托字段的包装器,这个包装器用来保护委托字段不被滥用
    • 包装器永远都不可能是被包装的东西

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

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

相关文章

Vue3之通过Vue.config.globalProperties注册全局属性

Vue3之通过Vue.config.globalProperties注册全局属性 文章目录 Vue3之通过Vue.config.globalProperties注册全局属性1. Vue.config.globalProperties2. 注册全局属性1. 注册方式12. 注册方式2 3. 在setup函数中获取 1. Vue.config.globalProperties Vue2中使用Vue.prototype.自…

面向对象(精髓)变继承关系为组和关系(_Decorator模式)

在软件开发中&#xff0c;设计模式是解决常见问题的可重用解决方案。在面向对象编程中&#xff0c;继承和组合是两种常用的代码复用方式。然而&#xff0c;随着软件需求的不断变化&#xff0c;我们需要更灵活的设计方式来应对不断变化的需求。在本文中&#xff0c;我们将讨论从…

计算机网络——TCP/IP网络层次模型

计算机网络——TCP/IP网络层次模型 TCP/IP网络模型的起源TCP/IP网络层次的结构TCP/IP如何交互 TCP/IP协议栈TCP/IP协议栈主要协议 TCP/IP 和 OSI之间的区别面向连接和无连接面向连接三次握手&#xff0c;四次挥手 无连接 我们上一次了解了OSI的网络层次模型&#xff0c;如果还没…

Linux学习-内存管理

目录 内存管理 malloc free 使用 字符串存储申请堆区 自主输入个数&#xff0c;然后通过malloc在程序中申请空间&#xff0c;不用必须提前指定大小 内存溢出 内存泄漏 内存碎片 内存管理 函数名就是指向该函数的函数指针。 堆区是自低向高&#xff0c;栈区是自高向低…

代理模式的学习

1. 概念 1.代理模式是什么&#xff1f; 其实就是选一个中间人&#xff0c;可以理解为中介&#xff0c;如果要买房子&#xff0c;先和中介去商量&#xff0c;中介和房主去商量&#xff0c;这样。 2.为什么需要代理模式&#xff1f; 其实还是为了安全吧&#xff0c;代理模式中…

ffmpeg日记4001-原理介绍-视频切割原理

原理 打开输入---->打开输出---->根据输入来创建流---->拷贝流设置---->循环读帧---->判断时间点是否到达切割点&#xff0c;并做设置---->设置pts和dts---->写入---->善后 重点是pts和dts如何设置。参考《ffmpeg学习日记25-pts&#xff0c;dts概念的…

ftp速度太慢只有几十k,怎么解决?

FTP是目前许多企业日常运营中还在用的文件传输方式。虽然比较普遍&#xff0c;然而&#xff0c;许多用户在使用FTP时经常遇到速度缓慢的问题&#xff0c;有时甚至只有几十KB/s。这不仅影响工作效率&#xff0c;还可能导致许多数据传输的延迟的问题。本文将探讨FTP速度慢的原因&…

计算机服务器中了faust勒索病毒怎么解密,faust勒索病毒解密工具流程

在互联网飞速发展的今天&#xff0c;越来越多的企业走向了数字化办公模式&#xff0c;许多企业开始利用网络计算机开展各项工作业务&#xff0c;网络也为企业的生产效率提供了极大便利&#xff0c;但网络中存在许多恶意威胁。近日&#xff0c;云天数据恢复中心接到许多企业的求…

关于一个数组的小细节

机缘 写一个矩阵转置的代码用到了数组 收获 了解到输入数组的大小要在数组前面而不能先定义数组然后再输入 举例 #include <stdio.h>int main() {int a, b;scanf("%d %d ",&a,&b);int arr[a][b];for(int i 0;i < a;i){for(int j 0;j < b…

记录启动Dubbo-admin遇到的问题

记录启动Dubbo-admin遇到的问题 dubbo-admin-ui 下载node_modules出现镜像问题打工程包时出现错误运行jar包时出现连接不上注册中心 dubbo-admin-ui 下载node_modules出现镜像问题 进行dubbo-admin-ui打包操作是在2024-03-11&#xff0c;原域名链接: https://registry.npm.tao…

Java 学习和实践笔记(34):对象的转型(casting)

对象的转型&#xff08;casting)有两种&#xff0c;一种是向上转型&#xff0c;一种是向下转型。 向上转型&#xff1a;父类引用指向子类对象。这属于自动类型转换&#xff0c;编译器会自动完成。 上一节的多态中&#xff0c;形参为父类Animal, 但是调用时实参为子类对象Dog&…

linux中查看并修改日期

1.如何在终端控制行界面显示并且调整日期&#xff1a; 显示当前日期&#xff1a; 2.显示当前年份&#xff1a; 3.显示当前月份&#xff1a; 4.显示当前天数&#xff1a; 5.显示到目前为止的天数&#xff1a; 6.显示日期&#xff1a; date “%x“ ≈ date ”%Y %m %d"…

09-设计模式 面试题

你之前项目中用过设计模式吗? 工厂方法模式分类 简单工厂模式工厂方法模式抽象工厂模式工厂模式 需求:设计一个咖啡店点餐系统。 设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】);再设计一个咖啡店类(CoffeeStore)…

Playwright中page.locator快速查找网页元素和对象交互操作

Playwright 是一个用于自动化测试和网页操作的 Python 库&#xff0c;它提供了对浏览器的控制和操作的功能&#xff0c;包括 Chromium、Firefox 和 WebKit。使用 Playwright&#xff0c;您可以编写自动化测试、网页截图、网页数据提取以及网页交互等任务。Playwright 具有以下特…

记一次实战项目所学(通用接口篇)

记一次实战项目所学&#xff08;通用接口篇&#xff09; 1.加解密工具&#xff08;AES&#xff0c;RSA&#xff0c;MD5&#xff09; 账号登录时可用 a.引依赖 <dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactI…

一、什么是 HarmonyOS ?

HarmonyOS 是华为开发的一款面向未来的全场景分布式智慧操作系统&#xff0c;将逐步覆盖 18N 全场景终端设备。 对消费者而言&#xff0c;HarmonyOS 用一个“统一的软件系统”&#xff0c;从根本上解决消费者面对大量智能终端体验割裂的问题&#xff0c;为消费者带来统一、便利…

Kafka Stream入门

1. 什么是流式计算 流式计算&#xff08;Stream Processing&#xff09;是一种计算模型&#xff0c;旨在处理连续的数据流。与传统的批处理模型不同&#xff0c;流式计算可以实时或接近实时地处理和分析数据&#xff0c;这意味着数据在生成后不久就被处理&#xff0c;而不是存…

基于android的物业管理系统的设计与实现19.8

目录 基于android的物业管理系统的设计与实现 3 摘 要 3 Android property managemengt system 5 Abstract 5 1 绪论 6 1.1 选题背景 6 1.2 课题研究现状 6 1.3 设计研究主要内容 7 1.4 系统主要设计思想 8 2 开发环境 8 2.1 Android系统的结构 8 图2-1 Android系统架构图 9 2…

Python绘图-14绘制3D图(下)

14.7绘制3D等高线图个性化colormap 14.7.1图像呈现 14.7.2绘图代码 import numpy as np # 导入numpy库&#xff0c;numpy是Python的一个强大的数值计算扩展程序库&#xff0c;支持大量的维度数组与矩阵运算。 import matplotlib.pyplot as plt # 导入matplotlib的绘图模块p…

UDP编程及特点

目录 1.UDP编程流程 2.recvfrom()、sento() 3.代码演示 3.udp特点 1.UDP编程流程 socket()用来创建套接字&#xff0c;使用 udp 协议时&#xff0c;选择数据报服务 SOCK_DGRAM。sendto()用来发送数据&#xff0c;由于 UDP 是无连接的&#xff0c;每次发送数据都需要指定对端…