【C#语言入门】16. 委托详解

news/2024/4/13 11:42:51/文章来源:https://blog.csdn.net/Cziii/article/details/136531050

【C#语言入门】16. 委托详解

一、什么是委托

  • 委托(delegate)是函数指针的“升级版”
  • 一切皆地址
    • 变量(数据)是以某个地址为起点的一段内存中所储存的值
    • 函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令
  • 直接调用与间接调用
    • 直接调用:通过函数名来调用函数,CPU通过函数名直接获得函数所在地址并开始执行→返回
    • 间接调用:通过函数指针来调用函数,CPU通过读取函数指针存储的值获得函数所在地址并开始执行→返回
  • Java中没有与委托相对应的功能实体
  • 委托的简单实用
class Program
{static void Main(string[] args){Calculator calculator = new Calculator();Action action = new Action(calculator.Report);//使用了action这个委托指向了calculator.report这个方法calculator.Report();action.Invoke();action();//简便写法Func<int, int, int> func = new Func<int, int, int>(calculator.Add);Func<int, int, int> func = new Func<int, int, int>(calculator.Sub);int x = 100;int y = 50;int z = 0;z = func1.Invoke(x, y);//z = func1(x, y);//简便写法Console.WriteLine(z);z = func2.Invoke(x, y);Console.WriteLine(z);}
}class Calculator
{public void Report(){Console.WriteLine("I have three methods.");}public int Add(int a, int b){int result = a + b;return result;}public int Sub(int a, int b){int result = a - b;return result;}}

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

  • 委托是一种类(class),类是数据类型所以委托也是一种数据类型
  • 它的声明方式与一般的类不同,主要是为了照顾可读性和C/C++传统
  • 注意声明委托的位置,避免写错地方结果声明成嵌套类型
  • 委托与所封装的方法必须“类型兼容”——》返回值的数据类型一致,参数列表在个数和数据类型上一致(参数名不需要一样)
public delegate double Calc(double x, double y);class Program
{static void Main(string[] args){Calculator calculator = new Calculator();Calc calc1 = new Calc(calculator.Add);Calc calc2 = new Calc(calculator.Sub);Calc calc3 = new Calc(calculator.Mul);Calc calc4 = new Calc(calculator.Div);double a = 100;double b = 200;double c = 0;c = calc1(a, b);Console.WriteLine(c);c = calc2(a, b);Console.WriteLine(c);c = calc3(a, b);Console.WriteLine(c);c = calc4(a, b);Console.WriteLine(c);}
}class Calculator
{public double Add(double a, double b){double result = a + b;return result;}public double Sub(double a, double b){double result = a - b;return result;}public double Mul(double a, double b){double result = a * b;return result;}public double Div(double a, double b){double result = a / b;return result;}
}

三、委托的一般使用

  • 实例:把方法当做参数传给另一个方法
    • 正确使用方法1:模版方法,“借用“指定的外部方法来产生结果,相当于”填空题“,常常位于代码的中部,且此类型委托有返回值。
    • 正确使用方法2:回调(callback)方法,调用指定的外部方法,相当于“流水线”,常位于代码末尾,且此类型委托无返回值。
  • 注意:难精通+易使用+功能强大,一旦被滥用则后果非常严重
    • 缺点1:这是一种方法级别的紧耦合,实现工作中要谨慎使用
    • 缺点2:使可读性下降、debug的难度增加
    • 缺点3:把委托回调、异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护
    • 缺点4:委托使用不当有可能造成内存泄漏和程序性能下降

模板方法:product、box和warpfactory类都不需要再变化了,只要在productfactory增加即可。

Reuse,重复使用,也叫“复用”,代码的复用不但可以提高工作效率,还可以减少bug的引入,良好的复用结构式所有优秀软件所追求的共同目标之一。

internal class Program
{static void Main(string[] args){ProductFactory productFactory = new ProductFactory();WarpFactory warpFactory = new WarpFactory();Func<Product> func1 = new Func<Product>(productFactory.MakePizza);Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);Box box1 = warpFactory.WarpProduct(func1);Box box2 = warpFactory.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 WarpFactory
{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 MakeToyCar(){Product product = new Product();product.Name = "ToyCar";return product;}}

回调方法:

internal class Program
{static void Main(string[] args){ProductFactory productFactory = new ProductFactory();WarpFactory warpFactory = new WarpFactory();Func<Product> func1 = new Func<Product>(productFactory.MakePizza);Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);Logger logger = new Logger();Action<Product> log = new Action<Product>(logger.Log);Box box1 = warpFactory.WarpProduct(func1, log);Box box2 = warpFactory.WarpProduct(func2, log);Console.WriteLine(box1.Product.Name);Console.WriteLine(box2.Product.Name);}
}class Logger
{public void Log(Product product){Console.WriteLine("Product {0} created at {1}.Price is {2}.", product.Name, DateTime.UtcNow, product.Price);}
}class Product
{public string Name { get; set; }public double Price { get; set; }
}class Box
{public Product Product { get; set; }
}class WarpFactory
{public Box WarpProduct(Func<Product>getProduct, Action<Product> logCallback){Box box = new Box();Product product = getProduct.Invoke();if (product.Price >= 50){logCallback(product);}box.Product = product;  return box;}
}class ProductFactory
{public Product MakePizza(){Product product = new Product();product.Name = "Pizza";product.Price = 12;return product;}public Product MakeToyCar(){Product product = new Product();product.Name = "ToyCar";product.Price = 70;return product;}}

四、委托的高级使用

  • 多播(multicast)委托,一个委托内部封装多个方法
  • 隐式异步调用
    • 同步与异步的简介
      • 中英文的语言差异
      • 同步:你做完了我(在你的基础上)接着做
      • 异步:咱们两个同时做完
    • 同步调用与异步调用的对比
      • 每一个运行的程序是一个进程(process)
      • 每个进程可以有一个或者多个线程(thread)
      • 同步调用是在同一线程内
      • 异步调用的底层机理是多线程
      • 串行同步单线程,并行异步多线程
    • 隐式多线程 v.s. 显式多线程
      • 直接同步调用:使用方法名
      • 间接同步调用:使用单播/多播委托的Invoke方法
      • 隐式异步调用:使用委托的BeginInvoke
      • 显式异步调用:使用Thread或者Task
  • 应该适时地使用接口(interface)取代一些对委托的使用
    • Java完全地使用接口取代了委托的功能,即Java没有与C#中委托相对应的功能实体

这是单播委托,一个学生对应一个委托。

internal class Class1
{static void Main(string[] args){Student stu1 = new Student() {ID = 1, PenColor = ConsoleColor.Yellow};Student stu2 = new Student() {ID = 2, PenColor = ConsoleColor.Green};Student stu3 = new Student() {ID = 3, PenColor = ConsoleColor.Red };Action action1 = new Action(stu1.DoHomework);Action action2 = new Action(stu2.DoHomework);Action action3 = new Action(stu3.DoHomework);action1.Invoke();action2.Invoke();action3.Invoke();}
}class Student
{public int ID { get; set; }public ConsoleColor PenColor { get; set; }public void DoHomework(){for(int i = 0; i<5 ; i++){Console.ForegroundColor = this.PenColor;Console.WriteLine("Student {0} doing homework {1} hour(s).",this.ID, i);Thread.Sleep(1000);}}
}

这是多播委托,且多播委托的执行顺序是按照封装顺序来的。

internal class Class1
{static void Main(string[] args){Student stu1 = new Student() {ID = 1, PenColor = ConsoleColor.Yellow};Student stu2 = new Student() {ID = 2, PenColor = ConsoleColor.Green};Student stu3 = new Student() {ID = 3, PenColor = ConsoleColor.Red };Action action1 = new Action(stu1.DoHomework);Action action2 = new Action(stu2.DoHomework);Action action3 = new Action(stu3.DoHomework);action1 += action2;action1 += action3;action1.Invoke();}
}class Student
{
...
}

同步直接调用

internal class Class1
{static void Main(string[] args){Student stu1 = new Student() {ID = 1, PenColor = ConsoleColor.Yellow};Student stu2 = new Student() {ID = 2, PenColor = ConsoleColor.Green};Student stu3 = new Student() {ID = 3, PenColor = ConsoleColor.Red };stu1.DoHomework();stu2.DoHomework();stu3.DoHomework();for(int i = 0; i<5 ; i++){Console.ForegroundColor = this.PenColor;Console.WriteLine("Student {0} doing homework {1} hour(s).",this.ID, i);Thread.Sleep(1000);}}
}class Student
{
...
}

同步间接调用

internal class Class1
{static void Main(string[] args){Student stu1 = new Student() {ID = 1, PenColor = ConsoleColor.Yellow};Student stu2 = new Student() {ID = 2, PenColor = ConsoleColor.Green};Student stu3 = new Student() {ID = 3, PenColor = ConsoleColor.Red };Action action1 = new Action(stu1.DoHomework);Action action2 = new Action(stu2.DoHomework);Action action3 = new Action(stu3.DoHomework);action1.Invoke();action2.Invoke();action3.Invoke();for(int i = 0; i<5 ; i++){Console.ForegroundColor = this.PenColor;Console.WriteLine("Student {0} doing homework {1} hour(s).",this.ID, i);Thread.Sleep(1000);}}
}class Student
{
...
}

隐式间接异步调用,但是几个线程同时访问同一个资源的时候容易产生冲突从而导致错误

internal class Class1
{static void Main(string[] args){Student stu1 = new Student() {ID = 1, PenColor = ConsoleColor.Yellow};Student stu2 = new Student() {ID = 2, PenColor = ConsoleColor.Green};Student stu3 = new Student() {ID = 3, PenColor = ConsoleColor.Red };Action action1 = new Action(stu1.DoHomework);Action action2 = new Action(stu2.DoHomework);Action action3 = new Action(stu3.DoHomework);action1.BeginInvoke(null, null);//典型的异步调用action2.BeginInvoke(null, null);action3.BeginInvoke(null, null);for(int i = 0; i<5 ; i++){Console.ForegroundColor = this.PenColor;Console.WriteLine("Student {0} doing homework {1} hour(s).",this.ID, i);Thread.Sleep(1000);}}
}class Student
{
...
}

显式异步调用

internal class Class1
{static void Main(string[] args){Student stu1 = new Student() {ID = 1, PenColor = ConsoleColor.Yellow};Student stu2 = new Student() {ID = 2, PenColor = ConsoleColor.Green};Student stu3 = new Student() {ID = 3, PenColor = ConsoleColor.Red };Thread thread1 = new Thread(new ThreadStart(stu1.DoHomework));Thread thread2 = new Thread(new ThreadStart(stu2.DoHomework));Thread thread3 = new Thread(new ThreadStart(stu3.DoHomework));thread1.Start();thread2.Start();thread3.Start();for(int i = 0; i<5 ; i++){Console.ForegroundColor = this.PenColor;Console.WriteLine("Student {0} doing homework {1} hour(s).",this.ID, i);Thread.Sleep(1000);}}
}class Student
{
...
}

更为高级的显式异步调用

internal class Class1
{static void Main(string[] args){Student stu1 = new Student() {ID = 1, PenColor = ConsoleColor.Yellow};Student stu2 = new Student() {ID = 2, PenColor = ConsoleColor.Green};Student stu3 = new Student() {ID = 3, PenColor = ConsoleColor.Red };Task task1 = new Task(new Action(stu1.DoHomework));Task task2 = new Task(new Action(stu2.DoHomework));Task task3 = new Task(new Action(stu3.DoHomework));for(int i = 0; i<5 ; i++){Console.ForegroundColor = this.PenColor;Console.WriteLine("Student {0} doing homework {1} hour(s).",this.ID, i);Thread.Sleep(1000);}}
}class Student
{
...
}

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

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

相关文章

设计模式(十):抽象工厂模式(创建型模式)

Abstract Factory&#xff0c;抽象工厂&#xff1a;提供一个创建一系列相关或相互依赖对 象的接口&#xff0c;而无须指定它们的具体类。 之前写过简单工厂和工厂方法模式(创建型模式)&#xff0c;这两种模式比较简单。 简单工厂模式其实不符合开闭原则&#xff0c;即对修改关闭…

C#,入门教程(26)——数据的基本概念与使用方法

上一篇&#xff1a; C#&#xff0c;入门教程(25)——注释&#xff08;Comments&#xff09;你会吗&#xff1f;看多图演示&#xff0c;学真正注释。https://blog.csdn.net/beijinghorn/article/details/124681888 本文所述的知识基本上适用于C/C&#xff0c;java等其他语言。 …

Rethinking Data Augmentation for Image Super-resolution

文章目录 Rethinking Data Augmentation for Image Super-resolution:1.概述2.一些现有方法的分析3.cutblur4.MOA 各种策略的混合5.降噪6.cutblur 代码 Rethinking Data Augmentation for Image Super-resolution: A Comprehensive Analysis and a New Strategy 1.概述 根据…

【JavaScript】字符串练习

练习 1&#xff1a;"smyhvaevaesmyh"查找字符串中所有 m 出现的位置。 代码实现&#xff1a; var str2 smyhvaevaesmyh; for (var i 0; i < str2.length; i) {//如果指定位置的符号 "o"//str2[i]if (str2.charAt(i) m) {console.log(i);} }练习 2&…

蚂蚁SEO什么是蜘蛛池2024最新强势蜘蛛池

蜘蛛池是一种搜索引擎优化&#xff08;SEO&#xff09;策略&#xff0c;通过在互联网上建立大量的网站和链接&#xff0c;吸引搜索引擎的爬虫&#xff08;也称为“蜘蛛”&#xff09;访问&#xff0c;以提高网站的搜索排名和曝光率。以下是关于蜘蛛池的详细解释&#xff1a; 获…

【网站项目】202物流管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

排序算法及Arrays

冒泡排序 1.相邻的数据两两比较&#xff0c;小的放前面&#xff0c;大的放后面。 2.第一轮比较完毕后&#xff0c;最大值已经确定了&#xff0c;第二轮可以少循环一次&#xff0c;后面依次类推。 3.如果数组中有n个数据&#xff0c;总共我们只执行n-1轮的代码就可以。 pack…

K8S之实现业务的金丝雀发布

如何实现金丝雀发布 金丝雀发布简介优缺点在k8s中实现金丝雀发布 金丝雀发布简介 金丝雀发布的由来&#xff1a;17 世纪&#xff0c;英国矿井工人发现&#xff0c;金丝雀对瓦斯这种气体十分敏感。空气中哪怕有极其微量的瓦斯&#xff0c;金丝雀也会停止歌唱&#xff1b;当瓦斯…

vscode 使用ssh进行远程开发 (remote-ssh),首次连接及后续使用,详细介绍

在vscode添加remote ssh插件 首次连接 选择左侧栏的扩展&#xff0c;并搜索remote ssh 它大概长这样&#xff0c;点击安装 安装成功后&#xff0c;在左侧栏会出现远程连接的图标&#xff0c;点击后选择ssh旁加号便可以进行连接。 安装成功后vscode左下角会有一个图标 点击图…

基于springboot的迷你天猫商城设计与实现

目 录 摘 要 I Abstract II 引 言 1 1 系统开发技术 3 1.1 Springboot 3 1.2 MyEclipse 3 1.3 MySQL 3 1.4 Apache JMeter 3 1.5 系统开发背景 4 1.6 系统需求分析 4 1.7 本章小结 4 2 系统分析 5 2.1 技术可行性分析 5 2.2 系统经济可行性分析 5 2.3 系统功能需求分析 5 2.4 …

网工学习 DHCP配置-接口模式

网工学习 DHCP配置-接口模式 学习DHCP总是看到&#xff0c;接口模式、全局模式、中继模式。理解起来也不困难&#xff0c;但是自己动手操作起来全是问号。跟着老师视频配置啥问题没有&#xff0c;自己组建网络环境配置就是不通&#xff0c;悲催。今天总结一下我学习接口模式的…

word文档-页眉下如何加一条横线(下边框线),不是下划线

word文档-页眉下如何加一条横线(下边框线)&#xff0c;不是下划线 加之前&#xff1a; 添加之后&#xff1a; 添加操作&#xff1a; "开始"菜单栏&#xff0c;点击边框进行添加&#xff0c;选择下框线。

FreeRTOS学习笔记——列表和列表项及其API函数讲解

目录 精华总结&#xff1a; 列表和列表项 架构脑图&#xff1a; 列表和列表项的简介 列表的结构 列表项 迷你列表项 列表和列表项的关系 FreeRTOS 列表和列表项相关 API 函数 架构脑图&#xff1a; 列表和列表项相关 API 函数 函数 vListInitialise() 函数 vListIni…

设计模式:六大原则 ③

一、六大设计原则 &#x1f360; 开闭原则 (Open Close Principle) &#x1f48c; 对扩展开放&#xff0c;对修改关闭。在程序需要进行拓展的时候&#xff0c;不能去修改原有的代码&#xff0c;实现一个热插拔的效果。简言之&#xff0c;是为了使程序的扩展性好&#xff0c;易…

像SpringBoot一样使用Flask - 4.拦截器

接上文《像SpringBoot一样使用Flask - 3.蓝图路由Blueprint》&#xff0c;我们已经整理了一个干净的"启动类"&#xff0c;现在要加入一些拦截器&#xff0c;为了方便统一管理。 一、常用的拦截器 # 拦截器 app.before_request def handle_before_request():"&qu…

Elasticsearch:机器学习与人工智能 - 理解差异

作者&#xff1a;来自 Elastic Aditya Tripathi, Jessica Taylor 长期以来&#xff0c;人工智能几乎完全是科幻小说作家的玩物&#xff0c;人类将技术推得太远&#xff0c;以至于它变得活跃起来 —— 正如好莱坞让我们相信的那样 —— 开始造成严重破坏。 令人愉快的东西&#…

[机器视觉]halcon十二 条码识别、字符识别之字符识别

[机器视觉]halcon十二 条码识别、字符识别之字符识别 流程 获取图像-》创建模型-》查找文本-》清除模型 效果 算子 create_text_model_reader &#xff1a; 创建文本模型 find_text : 查找文本 get_text_result &#xff1a;获取文本内容 set_text_model_param : 设置文本模板…

5G与智慧文旅的融合发展:推动旅游业转型升级与可持续发展

随着5G技术的飞速发展和广泛应用&#xff0c;其与智慧文旅的融合发展正成为推动旅游业转型升级与可持续发展的重要力量。5G技术以其高速率、低时延、大连接的特性&#xff0c;为智慧文旅注入了新的活力&#xff0c;助力旅游业实现更高效、更智能、更绿色的发展。本文将深入探讨…

大白话说---“消息队列”

目录 一、什么是消息队列&#xff1f; 二、消息队列的作用 1.解耦 2.削峰 3.异步 三、消息队列的使用场景 1.传统设计 2.加入消息队列后的优化 四、常见的消息队列 一、什么是消息队列&#xff1f; 从名称上&#xff0c;我们就可以得到两个关键信息&#xff0c;即“消息”和…

去电脑维修店修电脑需要注意什么呢?装机之家晓龙

每当电脑出现故障时&#xff0c;你无疑会感到非常沮丧。 如果计算机已过了保修期&#xff0c;您将无法享受制造商的免费保修服务。 这意味着您必须自费找到一家电脑维修店。 去电脑维修店并不容易。 大家一定要知道&#xff0c;电脑维修非常困难&#xff0c;尤其是笔记本电脑维…