C#学习 - 委托

委托

委托(delegate)是函数指针的升级版
委托是一种类(Class),类是数据类型,所以委托也是一种数据类型

static void Main(string[] args)
{Type t = typeof(Action);Console.WriteLine(t.IsClass);//输出True,则委托为类(Class)
}

直接调用与间接调用

  • 直接调用:使用函数名来调用函数,CPU通过函数名直接获得函数所在地址并开始执行
  • 间接调用:使用函数指针来调用函数,CPU通过读取函数指针存储的值获得函数所在地址并开始执行
    直接调用和间接调用效果一样
    C语言非函数指针直接调用
 #include <stdio.h>
int Add(int x, int y)//加法函数
{return x + y;
}
int Sub(int x, int y)//减法函数
{return x - y;
}
int main()
{int x = 100;int y = 200;int z;z = Add(x, y);//输入函数名直接调用printf("%d + %d = %d\n", x, y, z);z = Sub(x, y);printf("%d - %d = %d\n", x, y, z);return 0;
}

C语言使用函数指针间接调用

#include <stdio.h>
typedef int(*Cal)(int x, int y);//获得函数指针类型
int Add(int x, int y)
{return x + y;
}
int Sub(int x, int y)
{return x - y;
}
int main()
{int x = 100;int y = 200;int z;Cal funcPoint1 = &Add;//声明函数指针类型的变量Cal funcPoint2 = &Sub;z = funcPoint1(x, y);printf("%d + %d = %d\n", x, y, z);z = funcPoint2(x, y);printf("%d - %d = %d\n", x, y, z);return 0;
}

一切皆为地址

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

Action委托例子

Action委托只能对返回值为空的方法进行委托

internal class Program
{static void Main(string[] args){Calculator calculator = new Calculator();Action action = new Action(calculator.Report);//Action(void() target)//Action创建实例,后面圆括号内只能接一个返回值为空的方法calculator.Report();//直接调用action.Invoke();//间接调用action();//简写}
}
class Calculator
{public void Report(){Console.WriteLine("I'm Calculator.");}public int Add(int a, int b){return a + b;}public int Sub(int a, in int b) {return a - b;}
}

Function委托例子

Function委托有17种类型重载
代码中写为:Func

internal class Program
{static void Main(string[] args){Calculator calculator = new Calculator();Func<int, int, int> func1 = new Func<int, int, int>(calculator.Add);Func<int, int, int> func2 = new Func<int, int, int>(calculator.Sub);int x = 100;int y = 200;int z = 0;z = func1.Invoke(x, y);//或者写为:z = func1(x, y);Console.WriteLine(z);z = func2.Invoke(x, y);//或者写为:z = func2(x, y);Console.WriteLine(z);}
}
class Calculator
{public void Report(){Console.WriteLine("I'm Calculator.");}public int Add(int a, int b){return a + b;}public int Sub(int a, int b) {return a - b;}
}

C#自带的委托

即为 Action 和 Function 这两种委托,可以直接用

委托的声明

即为自定义委托
虽然委托是类,但委托声明方式与一般的类不同,声明更像是C/C++中的函数指针的声明,主要是为了增加可读性和继承C/C++的传统

public delegate double Cal(double x, double y);
//第一个double - 目标方法的返回类型
//圆括号内 - 目标方法的参数列表
internal class Program
{static void Main(string[] args){Calculator calculator = new Calculator();Cal cal1 = new Cal(calculator.Add);Cal cal2 = new Cal(calculator.Sub);Cal cal3 = new Cal(calculator.Mul);Cal cal4 = new Cal(calculator.Div);double x = 100;double y = 200;double z;z = cal1.Invoke(x, y);//".Invoke"可以删去Console.WriteLine(z);z = cal2.Invoke(x, y);Console.WriteLine(z);z = cal3.Invoke(x, y);Console.WriteLine(z);z = cal4.Invoke(x, y);Console.WriteLine(z);}
}
class Calculator
{public double Add(double x, double y){return x + y;}public double Sub(double x, double y){return x - y;}public double Mul(double x, double y){return x * y;}public double Div(double x, double y){return x / y;}
}

注意:委托与所封装的方法必须“类型兼容

delegate double Cal(double x, double y);
------------double Add(double x, double y) { return x + y; }
------------double Sub(double x, double y) { return x - y; }
------------double Mul(double x, double y) { return x * y; }
------------double Div(double x, double y) { return x / y; }

声明委托时不要放错位置,要声明在名称空间体中,以免被C#编译器认为是嵌套类(在类中声明一个类)

委托的一般使用

委托一般当作方法的参数传到另一个方法中去

  • 模板方法:借用指定的外部方法来产生结果
    • 可以增加代码重复使用率
internal class Program
{static void Main(string[] args){ProjectFactory projectFactory = new ProjectFactory();WrapFactory wrapFactory = new WrapFactory();Func<Project> func1 = new Func<Project>(projectFactory.MakeGame);Func<Project> func2 = new Func<Project>(projectFactory.MakeMovie);Box box1 = wrapFactory.WrapProject(func1);Box box2 = wrapFactory.WrapProject(func2);Console.WriteLine(box1.Project.Name);Console.WriteLine(box2.Project.Name);}
}
class Project//项目
{public string Name { get; set; }
}
class Box//项目封装箱
{public Project Project {  get; set; }
}
class WrapFactory//封装工厂
{public Box WrapProject(Func<Project> getProject){   //模板方法Box box = new Box();//创建一个封装箱Project project = getProject.Invoke();//获取一个项目box.Project = project;//将项目放进封装箱中return box;//返还封装箱}
}
class ProjectFactory//项目工厂
{public Project MakeGame(){Project project = new Project();project.Name = "Game";return project;}public Project MakeMovie(){Project project = new Project();project.Name = "Movie";return project;}
}
  • 回调(Callback)方法:调用指定的外部方法。一般位于主调方法的末尾,且一般没有返回值
//项目价格高于100时,打印生产项目时的标准时间
internal class Program
{static void Main(string[] args){ProjectFactory projectFactory = new ProjectFactory();WrapFactory wrapFactory = new WrapFactory();Func<Project> func1 = new Func<Project>(projectFactory.MakeGame);Func<Project> func2 = new Func<Project>(projectFactory.MakeMovie);Logger logger = new Logger();Action<Project> log = new Action<Project>(logger.Log);Box box1 = wrapFactory.WrapProject(func1, log);Box box2 = wrapFactory.WrapProject(func2, log);Console.WriteLine(box1.Project.Name);Console.WriteLine(box2.Project.Name);}
}
class Logger//记录程序运行状态
{public void Log(Project project){Console.WriteLine("Project \"{0}\" created at {1}, Price is {2}", project.Name, DateTime.UtcNow, project.Price);//DateTime.UtcNow - 不带时区的时间}
}
class Project//项目
{public string Name { get; set; }public double Price { get; set; }//项目价格
}
class Box//项目封装箱
{public Project Project {  get; set; }
}
class WrapFactory//封装工厂
{public Box WrapProject(Func<Project> getProject, Action<Project> logCallback){	//开始回调Box box = new Box();//创建一个封装箱Project project = getProject.Invoke();//获取一个项目if (project.Price > 100){logCallback(project);}box.Project = project;//将项目放进封装箱中return box;}
}
class ProjectFactory//项目工厂
{public Project MakeGame(){Project project = new Project();project.Name = "Game";project.Price = 298;return project;}public Project MakeMovie(){Project project = new Project();project.Name = "Movie";project.Price = 50;return project;}
}

注意:委托难精通、易使用、功能强大

  • 这是一种方法级别的紧耦合
  • 容易使可读性下降,增加debug难度
  • 把委托回调、异步调用、多线程纠缠在一起时,会让代码变得很难维护
  • 委托使用不当可能造成内存泄漏和程序性能下降

委托的高级使用

多播(Multicast)委托

即一个委托内封装着多个委托

  • 单播委托
    • 一个委托封装一个方法
//using System.Threading;
internal class Program
{static void Main(string[] args){Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };Action a1 = new Action(s1.DoHomework);Action a2 = new Action(s2.DoHomework);Action a3 = new Action(s3.DoHomework);a1.Invoke();a2.Invoke();a3.Invoke();}
}
class Student
{public int ID { get; set; }//学生IDpublic ConsoleColor PenColor { get; set; }//学生使用的笔的颜色public void DoHomework(){for (int i = 0; i < 5; i++){Console.ForegroundColor = this.PenColor;if (i == 0){Console.WriteLine("Student {0} starts doing homework.", this.ID);continue;}Thread.Sleep(1000);//1000ms = 1s//在哪个线程中调用了"Thread.Sleep",此线程就暂停 1sConsole.WriteLine("Student {0} has done homework {1} hour(s).", this.ID, i);}}
}
  • 多播委托
//将单播委托例子中的Main方法修改为如下
Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };
Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };
Action a1 = new Action(s1.DoHomework);
Action a2 = new Action(s2.DoHomework);
Action a3 = new Action(s3.DoHomework);a1 += a2;//将a2封装进a1
a1 += a3;//将a3封装进a1
a1.Invoke();//执行顺序为封装的先后顺序

异步调用

同步调用:一个人做完后,下一个人在前一个人做完的基础上接着做
异步调用:两个人同时做
同步调用与异步调用:每一个运行的程序是一个进程(Process),一个进程可以有一个或者多个线程(Thread)。同步调用是在一个线程内进行,异步调用是在多个线程中进行
同步调用是在单线程中进行串行调用
异步调用是在多线程中进行并行调用
可以利用委托进行隐式异步调用

  • 同步调用
//修改上段代码中的Main方法
Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };
Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };
s1.DoHomework();
s2.DoHomework();
s3.DoHomework();
//一个一个调用
//使用委托进行间接同步调用
Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };
Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };
Action a1 = new Action(s1.DoHomework);
Action a2 = new Action(s2.DoHomework);
Action a3 = new Action(s3.DoHomework);a1.Invoke();
a2.Invoke();
a3.Invoke();
//单播、多播委托都能同步调用
//a1 += a2;
//a1 += a3;
//a1.Invoke();for (int i = 0; i < 10; i++)
{Console.ForegroundColor = ConsoleColor.Cyan;Thread.Sleep(1000);Console.WriteLine("Main Thread {0}.", i);
}
  • 隐式异步调用
    • 分支线程是由BeginInvoke自动生成的
//修改上面的Main方法
//使用委托进行隐式异步调用
Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };
Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };Action a1 = new Action(s1.DoHomework);
Action a2 = new Action(s2.DoHomework);
Action a3 = new Action(s3.DoHomework);a1.BeginInvoke(null, null);//BeginInvoke会自动生成一个分支线程,再在分支线程中调用方法
//需要两个参数
//1. 异步调用的回调 - 调用完方法后后续的行动
//2. 向回调函数中传递参数
//不使用参数则写做 null
a2.BeginInvoke(null, null);
a3.BeginInvoke(null, null);
for (int i = 0; i < 10; i++)
{Console.ForegroundColor = ConsoleColor.Cyan;Thread.Sleep(1000);Console.WriteLine("Main Thread {0}.", i);
}
  • 显示异步调用
    • 手动生成线程
//使用Thread
Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };
Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };Thread t1 = new Thread(new ThreadStart(s1.DoHomework));
Thread t2 = new Thread(new ThreadStart(s2.DoHomework));
Thread t3 = new Thread(new ThreadStart(s3.DoHomework));t1.Start();
t2.Start();
t3.Start();for (int i = 0; i < 10; i++)
{Console.ForegroundColor = ConsoleColor.Cyan;Thread.Sleep(1000);Console.WriteLine("Main Thread {0}.", i);
}
//使用Task
Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };
Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };Task t1 = new Task(new Action(s1.DoHomework));
Task t2 = new Task(new Action(s2.DoHomework));
Task t3 = new Task(new Action(s3.DoHomework));t1.Start();
t2.Start();
t3.Start();for (int i = 0; i < 10; i++)
{Console.ForegroundColor = ConsoleColor.Cyan;Thread.Sleep(1000);Console.WriteLine("Main Thread {0}.", i);
}

使用接口取代委托

应适当使用接口(interface)取代一些对委托的使用,可以避免一些不必要的麻烦还可以有相同的效果

internal class Program
{static void Main(string[] args){IProjectFactory gameFactory = new GameFactory();IProjectFactory movieFactory = new MovieFactory();WrapFactory wrapFactory = new WrapFactory();Box box1 = wrapFactory.WrapProject(gameFactory);Box box2 = wrapFactory.WrapProject(movieFactory);Console.WriteLine(box1.Project.Name);Console.WriteLine(box2.Project.Name);}
}
interface IProjectFactory
{	//接口Project Make();
}
class GameFactory : IProjectFactory
{public Project Make(){Project project = new Project();project.Name = "Game";return project;}
}
class MovieFactory : IProjectFactory
{Project IProjectFactory.Make(){Project project = new Project();project.Name = "Movie";return project;}
}class Project//项目
{public string Name { get; set; }public double Price { get; set; }//项目价格
}
class Box//项目封装箱
{public Project Project {  get; set; }
}
class WrapFactory//封装工厂
{public Box WrapProject(IProjectFactory projectFactory){Box box = new Box();Project project = projectFactory.Make();box.Project = project;return box;}
}

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

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

相关文章

<Vue>使用依赖注入的方式共享数据

什么是vue依赖注入&#xff1f; Vue是一个用于构建用户界面的渐进式框架。 它提供了一种简单而灵活的方式来管理组件之间的数据流&#xff0c;即依赖注入&#xff08;Dependency Injection&#xff0c;DI&#xff09;。 依赖注入是一种设计模式&#xff0c;它允许一个组件从另一…

腾讯云CVM服务器标准型S5、SA3、S6详细介绍

腾讯云CVM服务器标准型实例的各项性能参数平衡&#xff0c;标准型云服务器适用于大多数常规业务&#xff0c;例如&#xff1a;web网站及中间件等&#xff0c;常见的标准型云服务器有CVM标准型S5、S6、SA3、SR1、S5se等规格&#xff0c;腾讯云服务器网txyfwq.com来详细说下云服务…

Synchronized与锁升级

一&#xff1a;java对象内存布局 对象在堆内存的存储布局可以划分为三个部分&#xff1a;对象头&#xff08;Header&#xff09;、实例数据&#xff08;Instance Data&#xff09; 和对齐填充 二&#xff1a;对象在堆内存中的存储布局 1&#xff1a;对象头 &#xff08;在64…

基础课23——设计客服机器人

根据调查数据显示&#xff0c;使用纯机器人完全替代客服的情况并不常见&#xff0c;人机结合模式的使用更为普遍。在这两种模式中&#xff0c;不满意用户的占比都非常低&#xff0c;不到1%。然而&#xff0c;在满意用户方面&#xff0c;人机结合模式的用户满意度明显高于其他模…

Redis02-持久化策略

目录 RDB&#xff08;Redis DataBase Backup file&#xff09; RDB执行原理 AOF&#xff08;Append-Only File&#xff09; RDB和AOF对比 Redis支持多种持久化方式&#xff0c;以确保数据在内存中持久存储&#xff0c;以便在Redis服务器重启时数据不会丢失。Redis中持久化的…

Android Studio(列表视图ListView)

前言 前面在适配器章节&#xff0c;已经介绍了ListView的作用(干什么的)&#xff0c;这节将主要介绍如何去设计ListView页面视图。 思考 列表视图需要些什么&#xff1f; 1. 列表项容器&#xff08;装载各列表项的容器&#xff09;&#xff1a;<ListView/> 2. 列表项布局…

MySQL数据脱敏(Data masking plugin functions)

对于企业而言&#xff0c;数据脱敏可以在数据共享或测试时用于保护敏感数据&#xff08;如信用卡&#xff0c;社保卡&#xff0c;地址等&#xff09;。通过对敏感数据进行脱敏处理&#xff0c;组织可以最大限度地降低数据泄露和未经授权访问的风险&#xff0c;同时仍能够使用真…

AI技术再刷屏!明星集体“说”外语,有何风险?

近日&#xff0c;一段美国歌手泰勒斯威夫特“说”中文的短视频在网络刷屏&#xff0c;引发热议。 视频中&#xff0c;泰勒斯威夫特“说”着流利中文&#xff0c;音色和讲母语时的音色类似&#xff0c;甚至连口型都能对上。 类似的视频还有很多外国人“说”地道中文、很多中国…

智能制造与网络升级:制造业企业应对未来的关键步骤

新一代信息技术正在和制造业加快融合发展&#xff0c;网络是企业数字化转型的基石&#xff0c;推进智能制造&#xff0c;需要升级网络支撑工业互联网平台的搭建、数字化车间、智能工厂的建设等等。 如何打造满足“智造”企业组网需求的网络&#xff1f; 企业总部、分支机构、工…

C++ 类的定义相关选择题及其答案

1、有以下程序&#xff1a; #include<iostream> using namespace std; class A{ public: A(){cout<<"A";} }; class B{public:B(){cout<<"B";}}; class C:public A{ B b; public: C(){cout<<"C";} }; int main(){ C ob…

figma-如何批量修改字体

一.选择字体 二.批量替换 编辑—>替换相同字体

UI设计工具都哪些常用的,推荐这5款

对于UI设计师来说&#xff0c;日常工作无非是围绕“需求分析”→设计实施→“开发交付”这三个环节来进行。 然而&#xff0c;在每个环节中&#xff0c;设计师使用的工具却完全不同。在这里&#xff0c;我收集整理了UI设计师在日常工作中常用的五种工具&#xff0c;希望能为新…

【ChatOCR】OCR+LLM定制化关键信息抽取(附开源大语言模型汇总整理)

目录 背景技术方案存在的问题及解决思路关键信息提取结果其他解决方案替换文心一言LangChain大型多模态模型&#xff08;Large Multimodal Model, LMM&#xff09; 开源大模型汇总LLaMA —— Meta 大语言模型Stanford Alpaca —— 指令调优的 LLaMA 模型Lit-LLaMA —— 基于 na…

数据结构线性表——单链表

前言&#xff1a;小伙伴们又见面啦&#xff0c;这篇文章我们来一起学习线性表的第二模块——单链表。 单链表的学习就要开始上强度啦&#xff0c;小伙伴们一定要努力&#xff0c;坚持&#xff01; 目录 一.什么是单链表 二.单链表与顺序表的区别 三.单链表的实现 1.单链表…

ClickHouse 学习之从高级到监控以及备份(二)

第 一 部分 高级篇 第 1 章 Explain 查看执行计划 在 clickhouse 20.6 版本之前要查看 SQL 语句的执行计划需要设置日志级别为 trace 才能可以看到&#xff0c;并且只能真正执行 sql&#xff0c;在执行日志里面查看。在 20.6 版本引入了原生的执行计划的语法。在 20.6.3 版本成…

【MongoDB】集群搭建实战 | 副本集 Replica-Set | 分片集群 Shard-Cluster | 安全认证

文章目录 MongoDB 集群架构副本集主节点选举原则搭建副本集主节点从节点仲裁节点 连接节点添加副本从节点添加仲裁者节点删除节点 副本集读写操作副本集中的方法 分片集群分片集群架构目标第一个副本集第二个副本集配置集初始化副本集路由集添加分片开启分片集合分片删除分片 安…

第23期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练 Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大型语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以…

Java 高效生成按指定间隔连续递增的列表(int,double)

简介 Java 按照指定间隔生成连续递增的List 列表&#xff08;引入Stream 类和流操作来提高效率&#xff09;&#xff1a; 1. 生成递增的List< Integer> Testpublic void test009(){int start 1;int interval 2;int count 10;List<Integer> list IntStream.ite…

华为云运维小结

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、pandas是什么&#xff1f; 一、pandas是什么&#xff1f; HCIP学习笔记-华为云运维方案-9&#xff1a;https://blog.csdn.net/GoNewWay/article/details/13152…

【亚马逊云科技产品测评】活动征文|亚马逊云科技AWS之EC2详细测评

引言 &#xff08;授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道&#xff09; 在当前的数字化时代&#xff0c;云服务已…