(六)CSharp-刘铁锰版-委托

一、委托概念

1、委托(delegate)是函数指针的“升级版”

  • 实例:C/C++ 中的函数指针(委托相当于函数指针)

一切皆地址

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

直接调用与间接调用

  • 直接调用:通过函数名来调用函数,CPU 通过函数名直接获得函数函数所在地址并开始执行->返回
  • 间接调用:通过函数指针来调用函数,CPU 通过读取函数指针存储的值获得函数所在地址并开始执行->返回

Java 中没有与委托相对应的功能实体

2、委托的简单使用

  • Action 委托(无带参)
  • Func 委托(带参)
    class Program
    {
        static void Main(string[] args)
        {

            Calculator calculator = new Calculator();
            Action action = new Action(calculator.Report);
            calculator.Report();//直接调用
            action.Invoke();
            action();//简便方法

            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);//func1(x, y);
            Console.WriteLine(z);
            z = func2.Invoke(x, y);//func2(x, y);
            Console.WriteLine(z);

        }
    }

 
    class Calculator
    {
        public void Report()
        {
            Console.WriteLine("I have 3 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;
        }

    }

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

  • 委托是一种类(class),类是数据类型所以委托也是一种数据类型(引用类型)
  Type t = typeof(Action);
  Console.WriteLine(t.IsClass);
  • 它的声明方式与一般的类不同,主要是为了照顾可读性和 C/C++ 传统
  • 注意声明委托的位置
    • 避免写错地方结果声明成嵌套类型(在类里,声明委托属于嵌套)
  • 委托与所封装的方法必需“类型兼容”
    delegate double Calc(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; }
- 返回值的数据类型一致
- 参数列表在个数和数据类型上一致(参数名不需要一样)

二、委托的一般使用

  • 实例:把方法当作参数传给另一个方法(函数式编程)
    • 正确使用1:模板方法,“借用”指定的外部方法来产生结果
      • 相当于“填空题”
      • 常位于代码中部
      • 委托有返回值
    • 正确使用2:回调(callback)方法,调用指定的外部方法
      • 相当于“流水线”
      • 常位于代码末尾
      • 委托无返回值
 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.MakeToyCar);

            Logger logger = new Logger();
            Action<Product> log = new Action<Product>(logger.Log);

            Box box1 = wrapFactory.WrapProduct(func1, log);
            Box box2 = wrapFactory.WrapProduct(func2, log);

            Console.WriteLine(box1.Product.Name);
            Console.WriteLine(box2.Product.Name);

        }
    }

    class Logger
    {
        public void Log(Product product)
        {
            Console.WriteLine("Product '{0}' create 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 WrapFactory
    {
        //模板方法
        //优点:复用性
        public Box WrapProduct(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 = "Ty Car";
            product.Price = 100;
            return product;
        }
    }
  • 注意:难精通+易使用+功能强大东西,一旦被滥用则后果非常严重
    • 缺点1:这是一种方法级别的紧耦合,现实工作中要慎之又慎
    • 缺点2:是可读性下降、debug 的难度增加
    • 缺点3:把委托回调、异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护
    • 缺点4:委托使用不当有可能造成内存泄漏和程序性能下降
      (实例方法,必定使用对象。使用委托,对象就不能被释放

面试官:你遇到的最烂的最难调试的代码有哪些?
答:委托的滥用

三、委托的高级使用

  • 1、多播(multicast)委托(间接同步调用)
  class Program
    {
        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();

            //单播委托
            //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);
            }
        }
    }
   
  • 2、隐式异步调用

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

    • Java 完全地使用接口取代了委托的功能,即 Java 没有与 C# 中委托相对应的功能实体
  class Program
    {
        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();
            stu1.DoHomework();
            stu1.DoHomework();
            //----------------

            //间接同步调用
            Action action1 = new Action(stu1.DoHomework);
            Action action2 = new Action(stu2.DoHomework);
            Action action3 = new Action(stu3.DoHomework);

            action1.Invoke();
            action2.Invoke();
            action3.Invoke();
            //----------------
            //隐式异步调用
            action1.BeginInvoke(null, null);
            action2.BeginInvoke(null, null);
            action3.BeginInvoke(null, null);
            //---------------
            //显示异步调用
            //第一种方式
            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();

            //第二种方式
            Task task1 = new Task(new Action(stu1.DoHomework));
            Task task2 = new Task(new Action(stu2.DoHomework));
            Task task3 = new Task(new Action(stu3.DoHomework));
            task1.Start();
            task2.Start();
            task3.Start();

            for (int i = 0; i < 5; i++)
            {
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("Main Thread {0}.", i);
                Thread.Sleep(1000);
            }

            Console.ReadKey();
        }
    }

    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);
            }
        }
    }

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

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

相关文章

使用阿里云OSS实现图片文件上传

说明&#xff1a;注册用户时&#xff0c;经常会用到上传头像。文件的上传/接收与一般文本数据不同。 一、创建Demo页面 先准备一个Demo页面 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>图片上传…

影响电磁铁磁力大小的因素有哪些

影响电磁铁磁力大小的因素主要有四个&#xff0c;一是缠绕在铁芯上线圈的圈数&#xff0c;二是线圈中电流的强度&#xff0c;三是缠绕的线圈与铁芯的距离&#xff0c;四是铁芯的大小形状。 首先要了解电磁铁的磁性是如何产生的&#xff0c;通电螺线管的磁场&#xff0c;由毕奥&…

总结895

学习目标&#xff1a; 月目标&#xff1a;6月&#xff08;线性代数强化9讲&#xff0c;背诵15篇短文&#xff0c;考研核心词过三遍&#xff09; 周目标&#xff1a;线性代数强化3讲&#xff0c;英语背3篇文章并回诵&#xff0c;检测 每日必复习&#xff08;5分钟&#xff09;…

JMM如何实现volatile写/读的内存语义

内存屏障类型表 StoreLoad Barriers是一个“全能型”的屏障&#xff0c;它同时具有其他3个屏障的效果。现代的多处理器大多支持该屏障&#xff08;其他类型的屏障不一定被所有处理器支持&#xff09;。执行该屏障开销会很昂贵&#xff0c;因为当前处理器通常要把写缓冲区中的数…

基于html+css的图展示112

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

【图书推荐 | 13】后端系列

【赠书活动第十二期 】 图书推荐 本期书籍&#xff1a;后端系列 图书列表 本期图书列表&#xff1a; Spring Cloud 微服务快速上手项目驱动零起点学JavaNode.js 从基础到项目实战Diango Web 开发实例精解Flask Web 全栈开发实战精通Hadoopsmysql 数据库基础与实战应用Neo4j 图谱…

【Hive】安装配置及导入Hdfs数据

知识目录 一、写在前面&#x1f495;二、Hive的安装与配置✨2.1 Hive简介2.2 上传与解压2.3 拷贝MySQL驱动2.4 hive-site.xml文件2.5 启动hive 三、导入Hdfs数据到Hive✨3.1 修改Hadoop集群配置3.2 初始化3.3 创建表3.4 从Hdfs导入数据 四、总结撒花&#x1f60a; 一、写在前面…

MySQL-索引详解(上)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️树高千尺&#xff0c;落叶归根人生不易&…

Qt(C++)绘制指针仪表盘显示当前温度

一、功能介绍 当前文章要实现的功能: 使用Qt绘制一个仪表盘,用来显示当前的温度,绘制刻度、绘制数字、绘制温度指针。仪表盘全程使用QPainter进行绘制,QPainter是Qt框架中非常重要的一个类,绘制功能的实现离不开它。如果想要使用Qt进行高质量的绘图或UI设计,必须掌握QP…

Django新手必看:如何创建应用和定义数据表。(详细讲解)

Django新手必看&#xff1a;如何创建应用和定义数据表。 1. Django创建应用1.1 创建应用1.2 应用的添加 2. Django ORM2.1 定义数据表2.2 定义项目数据表2.3 通用字段选项2.4 外键使用2.5 应用数据库迁移 &#x1f3d8;️&#x1f3d8;️个人简介&#xff1a;以山河作礼。 &…

学习HCIP的day.11

目录 十一、BGP的属性 1、权重属性 2、本地优先级 3、as-path 4、起源属性 5、MED --多出口的鉴别属性 十二、BGP选路规则 十三、BGP的社团属性 十四、BGP的在MA网络中的下一跳问题 五、BGP的认证 十一、BGP的属性 BGP协议在选路时&#xff0c;先对比属性&#xf…

Java(30天拿下---第一天)

Java开发&#xff08;30天拿下---第一天&#xff09; 一 hello world以及JDK,JRE,JVM二 转义字符三 注释四 代码规范五 DOS命令&#xff08;了解&#xff09;六 变量1.加号的使用2.数据类型整型浮点型字符类型布尔类型自动类型转换强制类型转换String类型 七 API文档 一 hello …

ASP.NET Core Web API入门之一:创建新项目

ASP.NET Core Web API入门之一&#xff1a;创建新项目 一、引言二、创建新项目三、加入Startup类&#xff0c;并替换Program.cs内容四、编辑Program.cs代码五、修改控制器的路由六、运行项目 一、引言 最近闲着&#xff0c;想着没真正从0-1开发过ASP.NET Core Web API的项目&a…

softmax之温度系数

1.数学表示 这是传统的softmax&#xff1a; q i e x p ( z i ) ∑ j e x p ( z j ) q_i \frac{exp(z_i)}{\sum_jexp(z_j)} qi​∑j​exp(zj​)exp(zi​)​ 或者写&#xff1a; q i e x p ( z i ) / 1.0 ∑ j e x p ( z j / 1.0 ) q_i \frac{exp(z_i)/1.0}{\sum_jexp(z_j/…

七、进程地址空间

一、环境变量 &#xff08;一&#xff09;概念 环境变量(environment variables)&#xff1a;系统当中用做特殊用途的系统变量。 如&#xff1a;我们在编写C/C代码的时候&#xff0c;在链接的时候&#xff0c;从来不知道我们的所链接的动态静态库在哪里&#xff0c;但是照样可…

vue3---模板引用 nextTick

目录 模板引用--ref 访问模板引用 v-for 中的模板引用 函数模板引用 组件上的 ref 简单理解Vue中的nextTick 示例 二、应用场景 三、nextTick源码浅析 实战 --- vue3实现编辑与查看功能 模板引用--ref 虽然 Vue 的声明性渲染模型为你抽象了大部分对 DOM 的直接操作&…

LeetCode - 15 三数之和

目录 题目来源 题目描述 示例 提示 题目解析 算法源码 题目来源 15. 三数之和 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满…

Cmake工具的简单使用

引言 本篇文章讲述如何简单的使用cmake工具构建一个项目&#xff0c;帮助入门的c新手学会如何使用cmake. 我们在Clion新创建一个项目时&#xff0c;会发现&#xff0c;除了main.cpp文件之外&#xff0c;还存在一个build-debug目录和一个CMakelists.txt文件&#xff0c;如图: …

群晖 NAS 外网访问设置 - 腾讯 DNSPod

目录 ​编辑 一、使用DNSPod&#xff0c;实现DDNS&#xff08;动态域名&#xff09; 二、公共概念厘清 三、腾讯DNSPod上详细设置步骤 1. 打开DNSPod.cn网站并登录 2. 登录成功后&#xff0c;选择【我的域名】-> 【添加域名】 3. 添加群晖NAS需要二级域名&#xff08…

安装 Kafka

文章目录 1.选择操作系统2.配置 Java 环境3.安装 ZooKeeper4.安装 broker&#xff08;1&#xff09;安装 broker&#xff08;2&#xff09;验证是否安装正确 5.配置 broker&#xff08;1&#xff09;常规配置&#xff08;2&#xff09;主题的默认配置 6.配置 Kafka 集群&#x…
最新文章