当前位置: 首页 > news >正文

委托(Delegate)与事件(Event)-(上篇)

C#中的委托(Delegate)是一种类型安全的函数指针,它允许将方法作为参数传递给其他方法,并且可以用来实现回调机制。委托是C#中实现事件处理、异步编程以及面向对象设计模式的重要工具之一。在C#中,委托被定义为引用类型,所有的委托都派生自System.Delegate类。

一、定义委托

定义一个委托需要使用delegate关键字,并指定返回类型和参数列表。

public delegate void MyDelegate(string message);

这里我们定义了一个名为MyDelegate的委托,它可以指向任何接受一个字符串参数且没有返回值的方法。

使用委托

1. 创建委托实例并关联方法

如果一旦定义了委托,就可以创建该委托类型的实例,并将其与具体的方法关联起来。

public class MyClass {public void PrintMessage(string message) {Console.WriteLine("PrintMessage: " + message);}
}public class Program {public static void Main() {// 创建委托实例,并关联到PrintMessage方法MyDelegate myDelegate = new MyDelegate(new MyClass().PrintMessage);// 调用委托myDelegate("Hello, World!");}
}
2. 简化委托实例化

从C# 2.0开始,可以直接将方法赋值给委托变量,而不需要显式调用委托构造函数:

MyDelegate myDelegate = new MyClass().PrintMessage;
// 或者更简单的方式:
MyDelegate myDelegate = new MyClass().PrintMessage;
3. 使用匿名方法
MyDelegate myDelegate = delegate(string message) {Console.WriteLine("Anonymous method: " + message);
};
4. 使用Lambda表达式

从C# 3.0开始,支持使用lambda表达式,这使得代码更加简洁:

MyDelegate myDelegate = message => Console.WriteLine("Lambda expression: " + message);
5. 多播委托

委托可以链接多个方法,形成所谓的多播委托或多路广播委托。当调用这样的委托时,所有链接的方法都会被依次调用:

MyDelegate del1 = new MyClass().PrintMessage;
MyDelegate del2 = message => Console.WriteLine("Another method: " + message);// 将两个委托连接起来
MyDelegate multiCastDelegate = del1 + del2;// 调用多播委托
multiCastDelegate("Calling multiple methods");

在这里需要注意的是,如果多播委托中任何一个方法抛出异常,则后续的方法将不会被执行。

6.内置委托类型

除了自定义委托外,C#还提供了一些内置的委托类型,比如ActionFuncPredicate,它们适用于不同的场景:

Action<T>:表示不返回任何结果的方法,可以有一个或多个输入参数。

Func<T, TResult>:表示返回结果的方法,可以有一个或多个输入参数。

Predicate<T>:表示返回布尔值的方法,通常用于搜索条件等。

泛型委托的基本概念

泛型委托允许在其定义中包含一个或多个类型参数,这些参数可以在实例化时指定具体的类型。这意味着你可以创建一个适用于多种类型的委托,而不必为每个特定类型都定义一个新的委托类型。例如,Func<T, TResult> 是一种常见的泛型委托,它接受一个输入参数 T 并返回一个结果 TResult;同样地,Action<T> 则是另一种泛型委托,它接受一个输入参数 T 但不返回任何值。

二、定义泛型委托

public delegate R MyGenericDelegate<T, R>(T param);

这里我们定义了一个名为 MyGenericDelegate 的泛型委托,它可以接受任意类型的单个参数,并返回任意类型的值。

使用预定义的泛型委托

.NET 框架已经提供了几种常用的泛型委托类型,如 Func<>Action<>Predicate<>,它们分别用于表示有返回值的方法、无返回值的方法以及返回布尔值的方法。这三种泛型委托覆盖了大部分日常编程的需求,因此大多数情况下你不需要自己定义新的泛型委托。

Func<>:用于定义带有返回值的方法签名。它可以有一个到十六个输入参数,并且最后一个类型参数总是表示返回值的类型。

Func<int, string> formatNumber = num => $"Number: {num}";
Console.WriteLine(formatNumber(42)); // 输出: Number: 42

Action<>:用于定义没有返回值的方法签名。它可以有一个到十六个输入参数。

Action<string> printMessage = message => Console.WriteLine($"Message: {message}");
printMessage("Hello World"); // 输出: Message: Hello World

Predicate<>:专门用于定义返回布尔值的方法签名,通常用来作为条件判断的标准。

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
Predicate<int> isEven = n => n % 2 == 0;
var evenNumbers = numbers.FindAll(isEven);

实例化与调用泛型委托

如果你想要使用上述提到的泛型委托时,可以通过直接赋值方法名给委托变量的方式来进行实例化,也可以使用 lambda 表达式或者匿名函数。一旦实例化完成,就可以像调用普通方法那样调用委托对象。

// 使用方法组转换简化语法
Func<int, int, int> add = AddNumbers;// 使用 Lambda 表达式
Func<int, int, int> multiply = (x, y) => x * y;// 调用委托
Console.WriteLine(add(2, 3));      // 输出: 5
Console.WriteLine(multiply(2, 3)); // 输出: 6

泛型委托中的变体支持

从 C# 4.0 开始,Func<>Action<> 支持协变和逆变特性,这使得我们可以更灵活地处理继承关系中的类型转换问题。具体来说,如果某个泛型委托的输出类型是从另一个类型派生出来的,则该泛型委托可以被赋值给以基类作为输出类型的委托变量(协变)。相反地,如果某个泛型委托的输入类型是基类,则它可以被赋值给以派生类作为输入类型的委托变量(逆变)。

class Animal { }
class Dog : Animal { }// 协变示例
Func<Animal> getDog = () => new Dog();
Animal animal = getDog(); // 正确,因为 Dog 继承自 Animal// 逆变示例
Action<Dog> feedDog = d => Console.WriteLine("Feeding dog.");
Action<Animal> feedAnimal = feedDog; // 正确,因为 Dog 继承自 Animal
feedAnimal(new Dog());

三、创建静态委托

静态委托并不是一个直接的语言特性或预定义类型,而是指将委托与静态方法关联起来使用的一种方式。

首先,你需要定义一个委托类型,它可以指向静态方法或实例方法。为了创建一个静态委托,你通常会将这个委托绑定到某个类中的静态方法上。

public class MathOperations {// 定义一个接受两个整数并返回一个整数的委托类型public delegate int Operation(int x, int y);// 静态方法 Add,符合上述委托签名public static int Add(int a, int b) {return a + b;}// 实例方法 Subtract,同样符合委托签名public int Subtract(int a, int b) {return a - b;}
}

在实例中Operation 是一个委托类型,它能够封装任意一个接受两个整数参数并返回一个整数结果的方法。我们还定义了两个符合该委托签名的方法:一个是静态方法 Add,另一个是非静态(实例)方法 Subtract

使用静态方法实例化委托

我们可以创建 Operation 类型的委托实例,并将其与静态方法 Add 关联起来。这可以通过以下几种方式完成:

1.直接赋值:最简单的方式是直接给委托变量赋值为静态方法的名字。

MathOperations.Operation addDelegate = MathOperations.Add;

2.使用 new 关键字:也可以显式地使用 new 来创建委托实例。

MathOperations.Operation addDelegate = new MathOperations.Operation(MathOperations.Add);

3.Lambda 表达式:如果静态方法非常简单,可以考虑使用 lambda 表达式来代替命名的方法。

MathOperations.Operation addDelegate = (x, y) => x + y;

如果一旦创建了委托实例,就可以像调用普通方法一样调用它:

Console.WriteLine(addDelegate(5, 3)); // 输出: 8

在多播场景下的静态委托

C# 中的委托支持多播特性,这意味着你可以将多个方法链接到同一个委托实例上。对于静态委托而言,这意味着你可以同时注册多个静态方法,当调用委托时,所有已注册的方法都会被依次执行。不过需要注意的是,由于静态委托只涉及静态方法,因此它们不会携带任何实例上下文信息。

MathOperations.Operation operations = null;// 添加第一个静态方法
operations += MathOperations.Add;// 假设还有另一个静态方法 Multiply
operations += MathOperations.Multiply; // 假定存在这样的静态方法// 调用所有已注册的方法
operations(2, 3); // 先调用 Add 再调用 Multiply

注意事项

尽管静态委托提供了方便的方式来引用静态方法,但在某些情况下也存在局限性。例如,根据 C# 的一些提案讨论,所谓的“静态委托”可能指的是那些只能引用静态函数、不能引用对象成员方法,并且不能链接到其他非静态委托的情况。然而,这些限制主要出现在有关新特性讨论的背景下,并不是当前版本 C# 的实际行为。实际上,在现行标准下,静态委托仍然可以与其他类型的委托混合使用,并且可以在多播环境中工作良好。

此外,当你在一个委托上调用 GetInvocationList() 方法获取调用列表时,如果是静态方法,则委托的 Target 属性将是 null,因为静态方法不依赖于特定的对象实例

综上所述,静态委托在 C# 中是一个有用的工具,尤其是在需要跨不同组件之间传递消息或事件处理程序的情况下。通过合理设计,你可以利用静态委托简化代码结构,减少不必要的对象实例化,从而提高性能和可维护性。

相关文章:

委托(Delegate)与事件(Event)-(上篇)

C#中的委托&#xff08;Delegate&#xff09;是一种类型安全的函数指针&#xff0c;它允许将方法作为参数传递给其他方法&#xff0c;并且可以用来实现回调机制。委托是C#中实现事件处理、异步编程以及面向对象设计模式的重要工具之一。在C#中&#xff0c;委托被定义为引用类型…...

Scala根据身份证前两位数判断地区

方法一 val id "339005200101010928"// 取出id前两位 val province id.substring(0, 2) /*//println(province)if (province "42") {println("湖北")}else if (province "11") {println("北京")}else if (province &qu…...

freeswitch(开启支持视频H264通话)

亲测版本centos 7.9系统–》 freeswitch1.10.9 本人freeswitch安装路径(根据自己的路径进入) /usr/local/freeswitch/etc/freeswitch场景介绍: 内部默认是不支持的,视频通话,需要开启模块使用方法: 第一步:进入vars.xml 下面找到global_codec_prefs和outbound_codec_pr…...

启发式搜索算法和优化算法的区别

启发式搜索算法和优化算法在计算机科学中都有广泛的应用&#xff0c;但它们之间存在一些明显的区别。 一、定义与核心思想 启发式搜索算法 定义&#xff1a;启发式搜索算法是一类基于经验和直觉的问题求解方法&#xff0c;通过观察问题的特点&#xff0c;并根据某种指…...

数据结构初阶---二叉树---堆

一、树 1.树的概念 树是一种非线性的数据结构&#xff0c;由n(n≥0)个有限结点组成的一个有层次关系的集合。形状类似一棵倒挂的树&#xff0c;根朝上&#xff0c;分支向下。 根结点没有前驱结点&#xff0c;可以有n(n≥0)个后继结点。 其余结点被分为M个互不相交的集合&am…...

微信小程序中 crypto-js 加解密全攻略

一、引言 在微信小程序开发中&#xff0c;数据的安全至关重要。加解密技术在保护用户数据和应用程序的安全性方面起着关键作用。小程序在与服务器进行数据交互时&#xff0c;面临着数据泄露、篡改等安全风险。为了确保用户信息的安全&#xff0c;选择合适的加解密算法变得尤为…...

单片机的软件开发环境

单片机&#xff08;Microcontroller Unit, MCU&#xff09;是一种将计算机系统中的中央处理器&#xff08;CPU&#xff09;、存储器&#xff08;Memory&#xff09;、输入输出接口&#xff08;I/O&#xff09;等集成在一块芯片上的微型计算机。单片机因其体积小、成本低、功能强…...

【echarts】数据过多时可以左右滑动查看(可鼠标可滚动条)

1. 鼠标左右拖动 在和 series 同级的地方配置 dataZoom&#xff1a; dataZoom: [{type: inside, // inside 鼠标左右拖图表&#xff0c;滚轮缩放&#xff1b; slider 使用滑动条start: 0, // 左边的滑块位置&#xff0c;表示从 0 开始显示end: 60, // 右边的滑块位置&#xf…...

Python 实现对人的行为预测

引言 随着人工智能技术的快速发展&#xff0c;行为预测在多个领域如智能安防、自动驾驶、个性化推荐系统等中扮演着越来越重要的角色。通过分析历史数据并结合先进的机器学习算法&#xff0c;我们可以预测个体或群体的行为模式&#xff0c;从而做出更加智能和高效的决策。本文…...

使用枚举实现单例模式,不会反序列化破坏攻击,不会被反射破坏攻击。(附带枚举单例的简单实现)

原因分析 1.反序列化方法 ① jdk8中的Enum源码中对反序列化方法进行重写&#xff0c;抛出异常。 java.lang.Enum#readObject方法截图如下 ②java.io.ObjectInputStream#readObject 方法中的 readEnum 方法处理了枚举类型的反序列化&#xff0c;从而确保了枚举的单例特性。 …...

scala隐式转换

概念&#xff1a; 在Scala编程语言中&#xff0c;隐式转换是一种强大的功能&#xff0c;它允许程序在需要时自动转换数据类型或增强对象功能。这种转换通常是通过定义一个标记为implicit的函数来实现的&#xff0c;这个函数能够将一种类型转换为另一种类型。隐式转换的使用可以…...

Spring Boot 应用 “Connection is closed” 及 MySQL 空闲超时断开连接解决方案

在使用 Spring Boot MySQL HikariCP 的组合时&#xff0c;可能会在生产或测试环境中遭遇类似如下异常信息&#xff1a; org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [SELECT ...]; SQL state [nu…...

SLF4J框架原理及其实现方案

slf4j 是一个日志规范框架&#xff1b;基本上所有的 JAVA 日志都要实现这个规范&#xff1b;比如&#xff1a;Logback、log4j、log4j2&#xff1b;本文档记载如何实现 slf4j 规范&#xff1b;实现自己的日志框架&#xff1b; slf4j 分为两个部分&#xff0c;其中包含 &#xf…...

代码随想录-算法训练营-番外(图论01:图论理论基础,所有可到达的路径)

day01 图论part01 今日任务:图论理论基础/所有可到达的路径 代码随想录图论视频部分还没更新 https://programmercarl.com/kamacoder/图论理论基础.html#图的基本概念 day01 所有可达路径 邻接矩阵 import java.util.Scanner;import java.util.List;import java.util.ArrayL…...

【JAVA】Java项目实战—Java EE项目:企业资源规划(ERP)系统

在企业管理中&#xff0c;企业资源规划&#xff08;ERP&#xff09;系统是不可或缺的工具。它能够帮助企业高效管理各种资源&#xff0c;包括人力资源、财务资源和库存等。Java作为一种成熟的编程语言&#xff0c;因其跨平台特性、强大的生态系统以及良好的社区支持&#xff0c…...

springboot配置过滤器解决html资源路径和接口路径冲突问题

比如&#xff1a; html文件使用 / 接口路径使用 /api 首先配置文件里肯定配置范围最大的根路径 server:port: 80servlet:context-path: / 过滤器代码 Slf4j public class RequestSeparationFilter implements Filter {Overridepublic void init(FilterConfig filterConfi…...

在IDE中使用Git

我们在开发的时候肯定是经常使用IDE进行开发的&#xff0c;所以在IDE中使用Git也是非常常用的&#xff0c;接下来以IDEA为例&#xff0c;其他的VS code &#xff0c;Pycharm等IDE都是一样的。 在IDEA中配置Git 1.打开IDEA 2.点击setting 3.直接搜索git 如果已经安装了会自…...

【AIGC进阶-ChatGPT提示词副业解析】反向心理学在沟通中的运用:激将法的艺术

引言 在日常沟通和管理中&#xff0c;直接的表达方式并不总能达到预期效果。反向心理学&#xff0c;特别是其中的激将法&#xff0c;作为一种独特的沟通技巧&#xff0c;往往能在看似消极的表达中激发出积极的反应。本文将深入探讨反向心理学中激将法的运用技巧、实施策略及其…...

JeecgBoot passwordChange 任意用户密码重置漏洞复现

0x01 产品简介 Jeecg Boot是一个企业级低代码开发平台,基于前后端分离的架构,融合了SpringBoot、SpringCloud、Ant Design、Vue、Mybatis-plus、Shiro、JWT等多种主流技术,旨在帮助企业快速构建各种应用系统,提高开发效率,降低开发成本。采用最新主流的前后分离框架,使得…...

【智体OS】官方上新发布智体机器人:使用rtrobot智体应用远程控制平衡车机器人

【智体OS】官方上新发布智体机器人&#xff1a;使用rtrobot智体应用远程控制平衡车机器人 dtns.network是一款主要由JavaScript编写的智体世界引擎&#xff08;内嵌了three.js编辑器的定制版-支持以第一视角浏览3D场馆&#xff09;&#xff0c;可以在浏览器和node.js、deno、e…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

学习一下用鸿蒙​​DevEco Studio HarmonyOS5实现百度地图

在鸿蒙&#xff08;HarmonyOS5&#xff09;中集成百度地图&#xff0c;可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API&#xff0c;可以构建跨设备的定位、导航和地图展示功能。 ​​1. 鸿蒙环境准备​​ ​​开发工具​​&#xff1a;下载安装 ​​De…...

命令行关闭Windows防火墙

命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)​方法二:CMD命令…...

Android Framework预装traceroute执行文件到system/bin下

文章目录 Android SDK中寻找traceroute代码内置traceroute到SDK中traceroute参数说明-I 参数&#xff08;使用 ICMP Echo 请求&#xff09;-T 参数&#xff08;使用 TCP SYN 包&#xff09; 相关文章 Android SDK中寻找traceroute代码 设备使用的是Android 11&#xff0c;在/s…...

C++ Saucer 编写Windows桌面应用

文章目录 一、背景二、Saucer 简介核心特性典型应用场景 三、生成自己的项目四、以Win32项目方式构建Win32项目禁用最大化按钮 五、总结 一、背景 使用Saucer框架&#xff0c;开发Windows桌面应用&#xff0c;把一个html页面作为GUI设计放到Saucer里&#xff0c;隐藏掉运行时弹…...