C# 入门教程(四)委托详解

文章目录

  • 1、什么是委托
  • 2、委托的声明(自定义委托)
  • 3、委托的使用
    • 3.1 实例:把方法当作参数传给另一个方法
    • 3.2 注意:难精通+易使用+功能强大东西,一旦被滥用则后果非常严重
  • 4、委托的高级使用
    • 4.1 多播(multicast)委托
    • 4.2隐式异步调用
      • 1. 同步与异步的简介
      • 2.同步调用与异步调用的对比
      • 3. 隐式多线程 vs 显式多线程

1、什么是委托

在 C#里,委托属于引用类型,其作用是封装和引用一个或多个方法。可以把它想象成一种类型安全的函数指针,不过它比函数指针更强大,因为它支持多播(也就是可以引用多个方法)。委托经常会在事件处理、回调函数以及异步编程中被用到。

  • 委托(delegate)是函数指针的"升级版"

    • 实例:C/C++中的函数指针
    #include <stdio.h>// 函数:加法
    int add(int a, int b) {return a + b;
    }// 函数:减法
    int subtract(int a, int b) {return a - b;
    }int main() {// 声明一个函数指针int (*operation)(int, int);// 让函数指针指向add函数operation = add;printf("加法结果: %d\n", operation(5, 3)); // 输出8// 让函数指针指向subtract函数operation = subtract;printf("减法结果: %d\n", operation(5, 3)); // 输出2return 0;
    }
    
  • 一切皆地址

    • 变量(数据)是以某个地址为起点的一段内存中所存储的值
    • 函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令
  • 直接调用与间接调用

    • 直接调用:通过函数名来调用函数,CPU通过函数名直接获得函数所在地址并开始执行→返回
    • 间接调用:通过函数指针来调用函数,CPU通过读取函数指针存储的值获得函数所在地址并开始执行→返回
  • Java中没有与委托相对应的功能实体

  • 委托的简单使用

    • Action委托
    • Func委托
    namespace DelegateExample
    {class Program{static void Main(string[] args){Calculatator calculatator = new Calculatator();//无参数委托Action act = new Action(calculatator.Report);//打印出来的都是一样的act();act.Invoke();calculatator.Report();//带参数的委托Func<int, int, int> func = new Func<int, int, int>(calculatator.Add);int a = 13;int b = 10;int z = func.Invoke(a, b);Console.WriteLine(z);Func<int, int, int> func2 = new Func<int, int, int>(calculatator.Sub);int z1 = func2.Invoke(a, b);Console.WriteLine(z1);}}class Calculatator{public void Report(){Console.WriteLine("I have 3 Methods");}public int Add(int x, int y){return x + y;}public int Sub(int x, int y){return x - y;}}
    }

2、委托的声明(自定义委托)

  • 委托是一种类(class),类是数据类型所以委托也是一种数据类型
  • 它的声名方式与一般的类不同,主要是为了照顾可读性和C/C++传统
  • 注意声明委托的位置
    • 避免写错地方结果声明成嵌套类型
  • 委托与所封装的方法必需类型兼容

在这里插入图片描述

  • 返回值的数据类型一致
  • 参数列表在个数和数据类型上一致(参数名不需要一样)
namespace DelegateExample
{public delegate int Calc(int x, int y);class Program{static void Main(string[] args){Calculatator calculatator = new Calculatator();Calc calc1 = new Calc(calculatator.Add);Calc calc2 = new Calc(calculatator.Sub);Calc calc3 = new Calc(calculatator.mul);Calc calc4 = new Calc(calculatator.Div);int a = 10;int b = 5;int c = calc1.Invoke(a, b);Console.WriteLine(c);int c1 = calc2.Invoke(a, b);Console.WriteLine(c1);int c2 = calc3.Invoke(a, b);Console.WriteLine(c2);int c3 = calc4.Invoke(a, b);Console.WriteLine(c3);}}class Calculatator{public int Add(int x, int y){return x + y;}public int Sub(int x, int y){return x - y;}public int mul(int x, int y){return x * y;}public int Div(int x, int y){return x / y;}}
}

3、委托的使用

3.1 实例:把方法当作参数传给另一个方法

  • 正确使用1:模板方法,“借用”指定的外部方法来产生结果相当于“填空题”常位于代码中部委托有返回值
namespace DelegateExample
{public delegate int Calc(int x, int y);class Program{static void Main(string[] args){ProductFactory productFactory = new ProductFactory();WrapFactory wrapFactory = new WrapFactory();Func<Product> func1 = new Func<Product>(productFactory.MakePizza);Func<Product> func2 = new Func<Product>(productFactory.MakeCare);Box box1 = wrapFactory.WarpProduct(func1);Box box2 = wrapFactory.WarpProduct(func2);Console.WriteLine(box1.Product.Name);Console.WriteLine(box2.Product.Name);}}class Product{public string Name { get; set; }}class Box{public Product Product { get; set; }}class WrapFactory(){public Box WarpProduct(Func<Product> getProduct){Box box = new Box();Product product = getProduct.Invoke();box.Product = product;return box;}}class ProductFactory(){public Product MakePizza(){Product product = new Product();product.Name = "pizza";return product;}public Product MakeCare(){Product product = new Product();product.Name = "care";return product;}}
}
  • 正确使用2:回调(callback)方法,调用指定的外部方法相当于”流水线”常位于代码末尾委托无返回值
namespace DelegateExample
{public delegate int Calc(int x, int y);class Program{static void Main(string[] args){ProductFactory productFactory = new ProductFactory();WrapFactory wrapFactory = new WrapFactory();Func<Product> func1 = new Func<Product>(productFactory.MakePizza);Func<Product> func2 = new Func<Product>(productFactory.MakeCare);// 实例化一个日志类,进行价格大于50的时候,打印相关日志Logger logger = new Logger();Action<Product> Log = new Action<Product>(logger.Log);Box box1 = wrapFactory.WarpProduct(func1, Log);Box box2 = wrapFactory.WarpProduct(func2, Log);Console.WriteLine(box1.Product.Name);Console.WriteLine(box2.Product.Name);}}class Logger{public void Log(Product product){Console.WriteLine("产品名:{0},价格:{1},创建于:{2}", product.Name, product.Price, DateTime.UtcNow);}}class Product{public string Name { get; set; }public decimal Price { get; set; }}class Box{public Product Product { get; set; }}class WrapFactory(){public Box WarpProduct(Func<Product> getProduct, Action<Product> LogCallback){Box box = new Box();Product product = getProduct.Invoke();if (product.Price > 50){LogCallback.Invoke(product);}box.Product = product;return box;}}class ProductFactory(){public Product MakePizza(){Product product = new Product();product.Name = "pizza";product.Price = 10;return product;}public Product MakeCare(){Product product = new Product();product.Name = "care";product.Price = 100;return product;}}}

3.2 注意:难精通+易使用+功能强大东西,一旦被滥用则后果非常严重

  • 缺点1:这是一种方法级别的紧耦合,现实工作中要慎之又慎
  • 缺点2:使可读性下降、debug的难度增加
  • 缺点3:把委托回调、异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护
  • 缺点4:委托使用不当有可能造成内存泄漏和程序性能下降

4、委托的高级使用

4.1 多播(multicast)委托

多播委托(Multicast Delegate) 是一种特殊的委托类型,它可以同时引用多个方法,当委托被调用时,会依次执行所有被引用的方法。这一特性使得多播委托成为实现事件机制、回调链等场景的核心基础。

多播委托通过将多个方法组合成一个可调用的实体,实现了 “一次调用,多方响应” 的效果,是 C# 中实现事件、回调等功能的核心机制。

namespace DelegateExample
{public delegate int Calc(int x, int y);class Program{static void Main(string[] args){Student student = new Student() { Id = 1, Color = ConsoleColor.Gray };Student student1 = new Student() { Id = 2, Color = ConsoleColor.Red };Student student2 = new Student() { Id = 3, Color = ConsoleColor.Green };Action action1 = new Action(student.DoHomework);Action action2 = new Action(student1.DoHomework);Action action3 = new Action(student2.DoHomework);Console.WriteLine("====================单播委托======================");action1.Invoke();action2.Invoke();action3.Invoke();Console.WriteLine("====================多播委托======================");//多播委托action1 += action2;action1 += action3;action1.Invoke();}}class Student{public int Id { get; set; }public ConsoleColor Color { get; set; }public void DoHomework(){for (int i = 0; i < 5; i++){Console.ForegroundColor = this.Color;Console.WriteLine("Student {0} doing work {1} hour", this.Id, i);}}}
}

打印结果,多个单播委托和多播委托打印结果一致;

在这里插入图片描述

4.2隐式异步调用

1. 同步与异步的简介

  • 中英文的语言差异
  • 同步:你做完了我(在你的基础上)接着做
  • 异步:咱们两个同时做(相当于汉语中的“同步进行”)

在这里插入图片描述

2.同步调用与异步调用的对比

  • 每一个运行的程序是一个进程(process)
  • 每个进程可以有一个或者多个线程(thread )
  • 同步调用是在同一线程内异步调用的底层机理是多线程。
  • 串行同步单线程,并行异步多线程

3. 隐式多线程 vs 显式多线程

  • 直接同步调用:使用方法名
  • 间接同步调用:使用单播/多播委托的Invoke方法
namespace DelegateExample
{public delegate int Calc(int x, int y);class Program{static void Main(string[] args){Student student = new Student() { Id = 1, Color = ConsoleColor.Gray };Student student1 = new Student() { Id = 2, Color = ConsoleColor.Red };Student student2 = new Student() { Id = 3, Color = ConsoleColor.Green };//直接调用student.DoHomework();student1.DoHomework();student2.DoHomework();//单播委托间接调用Action action1 = new Action(student.DoHomework);Action action2 = new Action(student1.DoHomework);Action action3 = new Action(student2.DoHomework);action1.Invoke();action2.Invoke();action3.Invoke();//多播委托间接调用action1 += action2;action1 += action3;action1.Invoke();for (int i = 0; i < 10; i++) {Console.ForegroundColor = ConsoleColor.Cyan;Console.WriteLine("Main thread {0}", i);}}}class Student{public int Id { get; set; }public ConsoleColor Color { get; set; }public void DoHomework(){for (int i = 0; i < 5; i++){Console.ForegroundColor = this.Color;Console.WriteLine("Student {0} doing work {1} hour", this.Id, i);}}}
}

打印结果均相同

在这里插入图片描述

  • 隐式异步调用:使用委托的BeginInvoke
namespace DelegateExample
{public delegate int Calc(int x, int y);class Program{static void Main(string[] args){Student student = new Student() { Id = 1, Color = ConsoleColor.Gray };Student student1 = new Student() { Id = 2, Color = ConsoleColor.Red };Student student2 = new Student() { Id = 3, Color = ConsoleColor.Green };Action action1 = new Action(student.DoHomework);Action action2 = new Action(student1.DoHomework);Action action3 = new Action(student2.DoHomework);//.NET 5 及更高版本Action.BeginInvoke 受到了限制action1.BeginInvoke(null, null);action2.BeginInvoke(null, null);action3.BeginInvoke(null, null);for (int i = 0; i < 10; i++) {Console.ForegroundColor = ConsoleColor.Cyan;Console.WriteLine("Main thread {0}", i);}}}class Student{public int Id { get; set; }public ConsoleColor Color { get; set; }public void DoHomework(){for (int i = 0; i < 5; i++){Console.ForegroundColor = this.Color;Console.WriteLine("Student {0} doing work {1} hour", this.Id, i);}}}
}
  • 显式导步调用:使用Thread
using System.Threading;namespace DelegateExample
{public delegate int Calc(int x, int y);class Program{static void Main(string[] args){Student student = new Student() { Id = 1, Color = ConsoleColor.Gray };Student student1 = new Student() { Id = 2, Color = ConsoleColor.Red };Student student2 = new Student() { Id = 3, Color = ConsoleColor.Green };Thread thread1 = new Thread(new ThreadStart(student.DoHomework));Thread thread2 = new Thread(new ThreadStart(student1.DoHomework));Thread thread3 = new Thread(new ThreadStart(student2.DoHomework));thread1.Start();thread2.Start();thread3.Start();for (int i = 0; i < 10; i++){Console.ForegroundColor = ConsoleColor.Cyan;Console.WriteLine("Main thread {0}", i);}}}class Student{public int Id { get; set; }public ConsoleColor Color { get; set; }public void DoHomework(){for (int i = 0; i < 5; i++){Console.ForegroundColor = this.Color;Console.WriteLine("Student {0} doing work {1} hour", this.Id, i);}}}
}

打印结果:异步调用

在这里插入图片描述

  • Task 异步调用
using System.Threading;
using System.Threading.Tasks;namespace DelegateExample
{public delegate int Calc(int x, int y);class Program{static void Main(string[] args){Student student = new Student() { Id = 1, Color = ConsoleColor.Gray };Student student1 = new Student() { Id = 2, Color = ConsoleColor.Red };Student student2 = new Student() { Id = 3, Color = ConsoleColor.Green };Task task1 = new Task(student.DoHomework);Task task2 = new Task(student1.DoHomework);Task task3 = new Task(student2.DoHomework);task1.Start();task2.Start();task3.Start();for (int i = 0; i < 10; i++){Console.ForegroundColor = ConsoleColor.Cyan;Console.WriteLine("Main thread {0}", i);}}}class Student{public int Id { get; set; }public ConsoleColor Color { get; set; }public void DoHomework(){for (int i = 0; i < 5; i++){Console.ForegroundColor = this.Color;Console.WriteLine("Student {0} doing work {1} hour", this.Id, i);}}}
}

在这里插入图片描述

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

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

相关文章

React的基本语法和原理

3. React条件渲染某些情况下&#xff0c;姐妹的内容会根据不同的情况显示不同的内容&#xff0c;或者决定是否渲染某部分内容&#xff1a; 在React中&#xff0c;所有的条件判断和普通的JavaScript代码一致&#xff1b;常见的条件渲染的方式有哪些&#xff1f;方式一&#xff1…

如何在 Gradle 项目中添加依赖?(以添加 AndroidX 版本的 RecyclerView 为例)

1. 确保项目已启用 AndroidX RecyclerView 的现代版本属于 AndroidX 库&#xff0c;需确保项目已启用 AndroidX&#xff1a; 在 gradle.properties 中应有以下配置&#xff08;通常新建项目默认开启&#xff09;&#xff1a;android.useAndroidXtrue android.enableJetifiert…

深度学习与图像处理 | 基于PaddlePaddle的梯度下降算法实现(线性回归投资预测)

演示基于PaddlePaddle自动求导技术实现梯度下降&#xff0c;简化求解过程。01、梯度下降法梯度下降法是机器学习领域非常重要和具有代表性的算法&#xff0c;它通过迭代计算来逐步寻找目标函数极小值。既然是一种迭代计算方法&#xff0c;那么最重要的就是往哪个方向迭代&#…

负载均衡集群HAproxy

HAProxy 简介HAProxy 是一款高性能的负载均衡器和代理服务器&#xff0c;支持 TCP 和 HTTP 应用。广泛用于高可用性集群&#xff0c;能够有效分发流量到多个后端服务器&#xff0c;确保服务的稳定性和可扩展性。HAProxy 核心功能负载均衡&#xff1a;支持轮询&#xff08;round…

重生之我在10天内卷赢C++ - DAY 1

坐稳了&#xff0c;我们的C重生之旅现在正式发车&#xff01;请系好安全带&#xff0c;前方高能&#xff0c;但绝对有趣&#xff01;&#x1f680; 重生之我在10天内卷赢C - DAY 1导师寄语&#xff1a;嘿&#xff0c;未来的编程大神&#xff01;欢迎来到C的世界。我知道&#x…

[mind-elixir]Mind-Elixir 的交互增强:单击、双击与鼠标 Hover 功能实现

[mind-elixir]Mind-Elixir 的交互增强&#xff1a;单击、双击与鼠标 Hover 功能实现 功能简述 通过防抖&#xff0c;实现单击双击区分通过mousemove事件&#xff0c;实现hover效果 实现思路 &#xff08;一&#xff09;单击与双击事件 功能描述 单击节点时&#xff0c;可以触发…

c++-迭代器类别仿函数常用算法函数

C常用算法函数 1. 前置知识 1.1 迭代器的类别 C中&#xff0c;迭代器是 STL 容器库的核心组件之一&#xff0c;具有举足轻重的作用&#xff0c;它提供了一种 统一的方式来访问和遍历容器&#xff0c;而无需关心底层数据结构的具体实现。迭代器类似指针&#xff0c;但比指针更通…

Python深度学习框架TensorFlow与Keras的实践探索

基础概念与安装配置 TensorFlow核心架构解析 TensorFlow是由Google Brain团队开发的开源深度学习框架&#xff0c;其核心架构包含数据流图&#xff08;Data Flow Graph&#xff09;和张量计算系统。数据流图通过节点表示运算操作&#xff08;如卷积、激活函数&#xff09;&…

c# net6.0+ 安装中文智能提示

https://github.com/stratosblue/IntelliSenseLocalizer 1、安装tool dotnet tool install -g islocalizer 2、 安装IntelliSense 文件&#xff0c;安装其他net版本修改下版本号 安装中文net6.0采集包 islocalizer install auto -m net6.0 -l zh-cn 安装中英文双语net6.0采集包…

【建模与仿真】二阶邻居节点信息驱动的节点重要性排序算法

导读&#xff1a; 在复杂网络中&#xff0c;挖掘重要节点对精准推荐、交通管控、谣言控制和疾病遏制等应用至关重要。为此&#xff0c;本文提出一种局部信息驱动的节点重要性排序算法Leaky Noisy Integrate-and-Fire (LNIF)。该算法通过获取节点的二阶邻居信息计算节点重要性&…

指令微调Qwen3实现文本分类任务

参考文档&#xff1a; SwanLab入门深度学习&#xff1a;Qwen3大模型指令微调 - 肖祥 - 博客园 vLLM&#xff1a;让大语言模型推理更高效的新一代引擎 —— 原理详解一_vllm 原理-CSDN博客 概述 为了实现对100个标签的多标签文本分类任务&#xff0c;前期调用gpt-4o进行prom…

【机器学习-3】 | 决策树与鸢尾花分类实践篇

0 序言 本文将深入探讨决策树算法&#xff0c;先回顾下前边的知识&#xff0c;从其基本概念、构建过程讲起&#xff0c;带你理解信息熵、信息增益等核心要点。 接着在引入新知识点&#xff0c;介绍Scikit - learn 库中决策树的实现与应用&#xff0c;再通过一个具体项目的方式来…

【数字投影】折幕影院都是沉浸式吗?

折幕影院作为一种现代化的展示形式&#xff0c;其核心特点在于通过多块屏幕拼接和投影融合技术&#xff0c;打造更具包围感的视觉体验。折幕影院设计通常采用多折幕结构&#xff0c;如三折幕、五折幕等&#xff0c;利用多台投影机的协同工作&#xff0c;呈现无缝衔接的超大画面…

数据结构——图(三、图的 广度/深度 优先搜索)

一、广度优先搜索(BFS)①找到与一个顶点相邻的所有顶点 ②标记哪些顶点被访问过 ③需要一个辅助队列#define MaxVertexNum 100 bool visited[MaxVertexNum]; //访问标记数组 void BFSTraverse(Graph G){ //对图进行广度优先遍历&#xff0c;处理非连通图的函数 for(int i0;i…

直击WAIC | 百度袁佛玉:加速具身智能技术及产品研发,助力场景应用多样化落地

7月26日&#xff0c;2025世界人工智能大会暨人工智能全球治理高级别会议&#xff08;WAIC&#xff09;在上海开幕。同期&#xff0c;由国家地方共建人形机器人创新中心&#xff08;以下简称“国地中心”&#xff09;与中国电子学会联合承办&#xff0c;百度智能云、中国联通上海…

2025年人形机器人动捕技术研讨会将在本周四召开

2025年7月31日爱迪斯通所主办的【2025人形机器动作捕捉技术研讨会】是携手北京天树探界公司线下活动结合线上直播的形式&#xff0c;会议将聚焦在“动作捕捉软硬件协同&#xff0c;加速人形机器人训练”&#xff0c;将深度讲解多项核心技术&#xff0c;包含全球知名的惯性动捕大…

Apple基础(Xcode①-项目结构解析)

要运行设备之前先选择好设备Product---->Destination---->选择设备首次运行手机提示如出现 “未受信任的企业级开发者” → 手机打开 设置 ▸ 通用 ▸ VPN与设备管理 → 信任你的 Apple ID 即可ContentView 是 SwiftUI 项目里 最顶层、最主界面 的那个“页面”&#xff0…

微服务 02

一、网关路由网关就是网络的关口。数据在网络间传输&#xff0c;从一个网络传输到另一网络时就需要经过网关来做数据的路由和转发以及数据安全的校验。路由是网关的核心功能之一&#xff0c;决定如何将客户端请求映射到后端服务。1、快速入门创建新模块&#xff0c;引入网关依赖…

04动手学深度学习笔记(上)

04数据操作 import torch(1)张量表示一个数据组成的数组&#xff0c;这个数组可能有多个维度。 xtorch.arange(12) xtensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])(2)通过shape来访问张量的形状和张量中元素的总数 x.shapetorch.Size([12])(3)number of elements表…

MCU中的RTC(Real-Time Clock,实时时钟)是什么?

MCU中的RTC(Real-Time Clock,实时时钟)是什么? 在MCU(微控制器单元)中,RTC(Real-Time Clock,实时时钟) 是一个独立计时模块,用于在系统断电或低功耗状态下持续记录时间和日期。以下是关于RTC的详细说明: 1. RTC的核心功能 精准计时:提供年、月、日、时、分、秒、…