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

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.dtcms.com/a/307714.html

相关文章:

  • 国产芯+单北斗防爆终端:W5-D防爆智能手机,助力工业安全通信升级
  • Flutter Chen Generator - yaml配置使用
  • 一个清洁机器人的城市漂流记
  • C++面试5题--6day
  • 三维开放场景图助力机器人自主导航!Point2Graph:点云驱动的三维开放词汇场景图端到端机器人导航
  • Flutter 页面跳转及传参总结
  • Excel超级处理器,多个word表格模板中内容提取到Excel表格中
  • npm从入门到精通一篇全
  • 深度学习(鱼书)day07--误差反向传播(前四节)
  • [免费]基于Python的招聘职位信息推荐系统(猎聘网数据分析与可视化)(Django+requests库)【论文+源码+SQL脚本】
  • 无人机光伏巡检缺陷检出率↑32%:陌讯多模态融合算法实战解析
  • InfluxDB 与 Python 框架结合:Django 应用案例(三)
  • git使用秘诀(详解0到1)
  • C# 事件Event
  • python中高效构建提示词
  • 软件工程:软件复用
  • 当过滤条件不符合最左前缀时,如何有效利用索引? | OceanBase SQL 优化实践
  • Verilog实现RPC从机(配合AXI_Slave使用)
  • 消息队列学习-----消息消失与积压
  • 操作系统数据格式相关(AI回答)
  • 性能优化(二):JS内存泄漏“探案”:从闭包到事件监听的隐形杀手
  • 经典屏保问题 - 华为OD机试真题(Java 题解)
  • uniapp Vue3版本使用pinia存储持久化插件pinia-plugin-persistedstate对微信小程序的配置
  • Django模型迁移指南:从命令用法到最佳实践
  • 分布式微服务--万字详解 微服务的各种负载均衡全场景以注意点
  • Vue3 + Electron 技术栈下 MAC 地址获取的方法、准确性优化与应对策略
  • mac操作笔记
  • nuxt3: trpc-nuxt和sqlite导致的503错误
  • Python 动态属性和特性(使用动态属性转换数据)
  • 【烧脑算法】Dijkstra 算法:解决最短路问题