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

【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.mzph.cn/news/736750.shtml

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

相关文章

Python开源项目周排行 2024年第5周

Python 趋势周报&#xff0c;按周浏览往期 GitHub,Gitee 等最热门的Python开源项目&#xff0c;入选的项目主要参考GitHub Trending,部分参考了Gitee和其他。排名不分先后&#xff0c;都是当周相对热门的项目。 入选公式&#xff1d;70%GitHub Trending20%Gitee10%其他 关注微…

分治算法相关

分治算法&#xff08;Divide and Conquer&#xff09; 是一种解决问题的算法思想&#xff0c;它将一个大问题分解成若干个规模较小的子问题&#xff0c;然后分别解决每个子问题&#xff0c;最后将子问题的解合并成原问题的解。 以下是分治算法的一些相关知识点&#xff1a; …

杠杆和保证金关系,很简单,众汇一个表格解决

很多投资者都能一眼就可以理清杠杆和保证金的关系&#xff0c;但是众汇外汇也看到很多交易届的新手&#xff0c;有时还浪费精力在杠杆和保证金的关系上&#xff0c;白白损失了许多盈利的机会&#xff0c;其实很简单&#xff0c;今天众汇就一个表格解决这个问题。 在使用表格之…

nacos安装与启动

下载nacos 选择Nacos 版本的原因主要是参考了官方的版本说明&#xff1a; 下载完毕后&#xff0c;会得到一个名称为nacos-server-xx.zip的文件&#xff0c;解压缩后的目录结构如下。 bin&#xff1a;存放启动和关闭Nacos Server的脚本文件。conf&#xff1a;Nacos Server的配…

javascript 版 WinMerge

WinMerge.html&#xff1a; <!DOCTYPE html> <html> <head><title>WinMerge</title><meta charset"UTF-8"> </head> <body> <h1>文件比较</h1> <form><label for"file1">旧版本…

自然语言处理(NLP)—— 词义消歧与词语相似性(Word similarity)

1. 词义消歧 1.1 词义消歧的概念 词义消歧&#xff08;Word Sense Disambiguation, WSD&#xff09;的概念及其在自然语言处理中的应用之一。词义消歧是确定一个词在特定语境下使用时所指的具体含义。 在提到的例子中&#xff0c;“Do you believe in clubs for young people?…

java跨平台原理,jdk/jre/jvm之间的关系

1 java基本认识 1.1 java语言 语言&#xff1a;人与人交流沟通的方式。比如&#xff0c;你好、hello等。 计算机语言&#xff1a;人与计算机之间进行信息交流的一种特殊方式。比如&#xff0c;Java语言、C语言、C等。 1.2 java的来源 Java 是由 Sun Microsystems 公司于 1…

Docker安装的Redis并忘记了密码,怎么找回

如果你通过Docker安装的Redis并忘记了密码&#xff0c;通常情况下&#xff0c;密码会在启动Redis容器时通过配置文件或环境变量设置。查看或恢复忘记的密码通常有以下几种方法&#xff1a; 方法1&#xff1a;检查Docker容器的启动命令或环境变量 如果你在启动Redis容器时指定…

状态机高阶讲解-03

402 00:17:30,860 --> 00:17:34,610 那首先要说在前面的&#xff0c;就是说 403 00:17:36,590 --> 00:17:39,770 这个状态机实际上已经是一种 404 00:17:42,060 --> 00:17:45,320 描述行为上的差别了 405 00:17:45,820 --> 00:17:47,860 就是说&#xff0c;实际…

SpringBoot中MD5使用

SpringBoot中MD5使用 新建md5类 public final class MD5 {public static String encrypt(String strSrc) {try {char[] hexChars {0, 1, 2, 3, 4, 5, 6, 7, 8,9, a, b, c, d, e, f};byte[] bytes strSrc.getBytes();MessageDigest md MessageDigest.getInstance("MD5…

组态软件基础知识

一、组态软件基础知识 1、概述 &#xff08;1&#xff09;、组态软件概念与产生背景 “组态”的概念是伴随着集散型控制系统&#xff08;Distributed Control System简称DCS&#xff09;的出现才开始被广大的生产过程自动化技术人员所熟知的。在工业控制技术的不断发展和应用…

vue上传图片,直接回显本地的图片

<input name"file" type"file" change"update" /> <img :src"imgSrc" alt""> update(e) {const file e.target.files[0] || e.dataTransfer.files[0]let URL window.URL || window.webkitURL; // 当前地址th…

HR人才测评,如何测评候选人的心理素质?

心理素质&#xff0c;指的是处于压力之下&#xff0c;其解决问题的能力、团队协作能力&#xff0c;事务处理的效率等是否保持稳定和高效。心理素质决定了员工在岗位上的适应能力&#xff0c;也是发展潜力的基础。 对于企业来说&#xff0c;心理素质好的员工&#xff0c;不仅是…

哪些泛域名ssl证书一年送一月

泛域名SSL数字证书&#xff0c;也称之为通配符SSL证书&#xff0c;是数字证书中比较特殊的产品。与传统的单域名SSL证书不同&#xff0c;泛域名SSL证书能够同时保护多个域名站点&#xff0c;只对域名站点的类型有限制——只能保护主域名以及主域名下的所有子域名站点。今天就随…

C++设计模式-单例模式

目录 概述 懒汉模式 饿汉模式 懒汉式单例模式与多线程

数据安全与销毁:数据销毁实践指南

数据销毁处理 为了满足合规要求及组织机构本身的业务发展需求&#xff0c;组织机构需要对数据进行销毁处理。因为数据销毁处理要求针对数据的内容进行清除和净化&#xff0c;以确保攻击者无法通过存储介质中的数据内容进行恶意恢复&#xff0c;从而造成严重的敏感信息泄露问题。…

【C++从0到王者】第五十二站:跳表

文章目录 一、什么是跳表二、skiplist的效率三、skiplist的实现 一、什么是跳表 skiplist本质上也是一种查找结构&#xff0c;用于解决算法中的查找问题&#xff0c;跟平衡搜索树和哈希表的价值是一样的&#xff0c;可以作为key或者key/value的查找模型。 skiplist&#xff0c;…

三维的旋转平移矩阵形式

在三维空间中&#xff0c;一个物体或坐标系的旋转和平移可以通过一个4x4的变换矩阵来表示。这个矩阵通常被称为仿射变换矩阵或齐次变换矩阵。它结合了旋转矩阵和平移向量的功能&#xff0c;能够同时表示旋转和平移操作。 一个4x4的旋转平移矩阵通常具有以下形式&#xff1a; 复…

基于ElasticSearch存储海量AIS数据-架构设计篇

文章目录 引言I 背景II 数据集群架构选型2.1 关键点2.2 架构设计2.3 数据迁移工具:Canal关键词:时空索引;船舶自动识别系统;轨迹压缩;数据集群 引言 船舶自动识别系统(AIS)数据具有海量性、时空性和小记录频繁更新等特性。面向海量AIS数据,提出了一种基于分布式集群的AIS…

V1.2基站设置IP及设置基站连接服务器

基站状态指示灯 基站正常连接上服务器&#xff0c;基站指示灯如下&#xff0c;第一个灯是电源指示灯常亮&#xff1b;第二个灯为运行指示灯&#xff0c;程序正常运行第二个灯一直闪烁&#xff1b;第三个灯为为网络指示灯&#xff0c;网络连接正常会常亮&#xff0c;网络不通或…