C# 委托详解02(委托和事件单独开一篇)

上一篇仅仅简单通过代码,以及相关运行示例,对委托有了基本概念。这一篇侧重对委托的更加深入的理解。有问题欢迎评论。本人技术不高,也欢迎指正。

希望看完我能够解释清楚以下问题,而大家能够从中找到自己的答案。

  1. 什么是委托,如何理解委托 
  2. 委托的好处,为什么要使用委托
  3. 委托和事件的区别
  4. 为什么说委托是事件和回调方法的基础
  5. 如何理解事件是一种特殊的多播委托?
  6. 委托是如何使用的?委托的调用和函数的调用相比,其优势具体体现在什么地方?
  7. 在class的内部也可以申明委托? 此委托和在namespace下的委托有啥区别?
  8. 设计模式来讲,委托(类)提供了方法(对象)的抽象? 如何理解?
  9. 如果将委托理解为方法的抽象,那么和抽象类 基类又有什么区别
  10. 委托和接口又有什么区别, 接口和抽象类又有什么区别?为什么要弄两个概念,接口不够用吗,抽象类有什么独到的优势和特点?  如何理解接口是行为的抽象?

总述

对于委托还有没一点了解的,可以看我之前的一篇文章。

这篇文章,讲一下我最深刻的对于委托的理解:委托的使用, 完全可以理解为将函数作为参数传递。(这篇文章完全不讲事件,不牵扯事件,委托03将仔细讲委托和事件直接的关系)

有C基础的朋友,可以将委托理解为函数指针;有面向对象基础的朋友,则可以将其理解为一种引用类型的数据类型,就和class类一样,是C#中特有的一种数据类型。

如果class是面向对象的一个概念,将现实世界的实体对象化,那么委托(delegate)可以理解为一种将方法本身作为一个实体来操作的机制。在面向对象的世界里,我们通过类来定义对象的属性和行为,而委托则提供了一种将这些行为(即方法)独立出来,使其可以被动态地引用和执行的能力。

委托(Delegate)是一种特殊的类型,它可以持有对方法的引用。你可以把它想象成一个指向函数的指针,但它比指针更安全、更灵活。委托允许你将方法作为参数传递给其他方法,或者从方法中返回方法。

参考视频,十来分钟,可反复观看,讲解的非常好 

C#基础教程 delegate 帮你理解委托,知道委托的好处, 不懂委托一定要看下!_哔哩哔哩_bilibili

1. 委托的优势

委托从大方向来讲,主要有两个用途: 模板函数和回调函数 (核心都是把函数作为参数来传递)

模板函数类似于一个模块化的功能,都固定好了,留有一个填空,这个填空会在调用时传递什么,我就填什么,也就是执行调用时传入的函数。

回调函数有一个逻辑选择,即我调用你还是不调用你,里面的相关逻辑会触发回调。说白了,还是函数作为参数,只是叫法说法不一样罢了。

具体的:回调关系:某个方法可能会被使用,也可能不被调用

一群人给了我一把名片,当我需要帮助的时候,可以在这一把名片这个找到能给我提供帮助的人,然后打电话寻求帮助,打电话的过程其实就是回调的过程。调用那边的某个功能来帮我解决帮个问题。回调函数给我们了一个机会,让我们可以动态的选择将要被回调的方法(动态的选择的过程也就是委托的体现,把函数当作参数来传递)

委托调用其灵魂和核心:  1)函数指针  2)把函数巧妙转换成了实例化对象。委托类型的对象,就是函数指针。既然委托也是一种数据类型,这就意味着在编写函数的时候,可以传入委托这种数据类型的参数,而该参数是灵活可变动的,也就是说,在一个大函数中,传入的参数为委托类型,然后函数内部对该委托类型的参数做一些处理。而这个参数到底指向哪个函数,实例化哪个函数,是灵活可变动的。即:委托就是把函数当作参数来传递和使用!!!

举例(模板函数):

Handle 就是某个处理模块的一整套流程:
比方说,一家公涉及到设计 、生产、 销售 三个流程。设计和销售是不变的,都是由该公司去实现完成,生产过程需要外包委托给其它公司,而Handle函数就是模拟设计生产销售一整套流程。让用户输入数字就是设计, 而通过委托来调用不同的函数就是生产,最后控制台输出可以理解为销售。这样,当公司要执行业务流程的时候,只需要在main函数中直接调用Handle函数,并给出需要委托的生产商(即Handle函数传参到底是哪个),一切都很顺畅的执行。

    //在不同的情况可能需要使用不同的处理方法,所以将处理写成委托形式,可以传参调用不同的处理
public void Handle(Cal cal) 
    {
        //设计
        double a;
        double b;
        Console.WriteLine("输入第一个数字:");
        a = Convert.ToDouble(Console.ReadLine());
        Console.WriteLine("输入第二个数字:");
        b = Convert.ToDouble(Console.ReadLine());
        //生产委托给别人
        double result = cal.Invoke(a, b);
        result += 5; //后续处理
        Console.WriteLine($"处理的结果为{result}"); //销售
    }

比方说想要委托富士康厂商来生产,就在main函数实现这个流程的时候,即调用Handle函数传参的时候传入需要的生产商,即参数即可。(结尾附完整代码,不懂的uu可以自行取用)

   static void Main(string[] args)
   {
       Program prom = new Program();
       Calculate calculate = new Calculate();
       prom.Handle(calculate.Add); //找委托商 并且让委托商来帮我们做事
   }

这么做的优势:Handle函数就固定下来了,形成一个功能模块化的东西,不用去到函数内部,仅通过外部的修改参数就可以实现委托生产商的改变。在大型项目中,Handle函数很有可能是别人先修改,你接触不到,只需要调用即可;Handle也大概率是封装起来的,如果每次生产商变了就到函数内部去修改,其灵活性和可变动行太差了,一点也不利于维护!

2. 委托和直接调用方法本身,其本质区别

直接函数调用:直接调用函数时,函数的调用是固定的,不能在运行时改变。

委托调用:委托可以在运行时动态地改变其指向的方法,这使得委托非常灵活,可以根据运行时的条件来决定调用哪个方法。

直接调用函数是指直接通过方法名来调用方法。这种方式编译时就确定了方法的调用关系,编译器会将方法调用转换为对方法入口点的直接调用指令。                  

void Method() { /* ... */ }
Method();

在这种情况下,编译器生成的IL(中间语言)代码会包含一个直接调用指令(call),直接跳转到Method方法的入口点。

使用委托调用函数时,实际上是通过委托实例来间接调用方法。这种方式在运行时才确定具体调用哪个方法。  (关于编译时和运行时下区别可以简单理解:编译时:代码——>exe可执行文件的过程;运行时:点击执行exe文件的过程)

delegate void MyDelegate();
class Program {
    static void Method() { /* ... */ }
    static void Main() {
        MyDelegate d = new MyDelegate(Method);
        d(); // 通过委托调用Method
    }
}

在这种情况下,编译器生成的IL代码会包含一个对委托实例上Invoke方法的调用指令(callvirt),这是一个虚拟调用,因为委托可以引用任何符合委托签名的方法。

  • 直接调用函数没有灵活性,因为它在编译时就确定了调用的目标。
  • 委托调用提供了高度的灵活性,可以在运行时动态地改变要调用的方法。
  • 在运行时,用户的不同操作会使得触发不同的委托函数执行不同的流程!!!

附: 完整代码

namespace ConsoleDel
{
    public delegate double Cal(double a, double b);  //声明委托
    internal class Program
    {
        static void Main(string[] args)
        {
            Program prom = new Program();
            Calculate calculate = new Calculate();
            Cal cal = new Cal(calculate.Del);
            prom.Handle(calculate.Add); //找委托商 并且让委托商来帮我们做事
            prom.Handle(cal); //换生产商(另一种写法)
        }
        #region 委托:函数作为参数传递实例
        //Handle 就是某个处理模块的一整套流程:
        //一家公涉及到设计 生产 销售 三个流程,设计和销售是不变的,都是由该公司去实现完成,生产过程需要外包委托给其它公司
        //1 .获取用户归纳兴趣的数字 (让用户输入数字就是设计)
        //2. 对获取的数据做相关处理  (通过委托来调用不同的函数就是生产)
        //3. 处理后拿到结果,做下一步的规划处理
        //4. 销售 (最后控制台输出可以理解为销售)

        //  这样,当公司要执行业务流程的时候,只需要在main函数中直接调用Handle函数,并给出需要委托的生产商,一切都很顺畅的执行。

        //前后的一些模块同固定好了,就是中间不知道会用哪个方法,所以先写成流程化的东西,要改变的通过参数传递的方式去实现

        #endregion

        #region 具体实例
        public void Handle(Cal cal)
        {
            //设计
            double a;
            double b;
            Console.WriteLine("输入第一个数字:");
            a = Convert.ToDouble(Console.ReadLine());
            Console.WriteLine("输入第二个数字:");
            b = Convert.ToDouble(Console.ReadLine());
            //生产委托给别人 
            double result = cal.Invoke(a, b);
            result += 5; //后续处理
            Console.WriteLine($"处理的结果为{result}"); //销售
        }
        #endregion

    }
}

namespace ConsoleDel
{
    /// <summary>
    /// 计算类,委托的扩展性和复用性
    /// </summary>
    public class Calculate
    {
        public void Report()
        {
            Console.WriteLine("there are five methods!");
        }
        public double Add(double a, double b)
        {
            Console.WriteLine($"del a+b = :+ {a + b}");
            return a + b;
        }
        public double Sub(double a, double b)
        {
            Console.WriteLine($"del a-b = :+ {a - b}");
            return a - b;
        }
        public double Mul(double a, double b)
        {
            Console.WriteLine($"del a*b = :+ {a * b}");
            return a * b;
        }

        public double Del(double a, double b)
        {
            Console.WriteLine($"del a/b = :+ {a / b}");
            return a / b;
        }
    }
}

效果如下

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

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

相关文章

【零成本抽象】基本概念与在C++中的实现

零成本抽象概念是由 Bjarne Stroustrup 提出的,他在 1994 年的著作中就有相关设想,2016 年其在 C++ 大会登台演讲时,明确阐述了 C++ 中的 “零成本抽象” 这一理念。 一、零成本抽象概念 Bjarne Stroustrup提出的零成本抽象概念,是指在编程中使用高级抽象机制时,不会产生…

Docker 安装 Jenkins:2.346.3

准备&#xff1a;已安装Docker&#xff0c;已配置服务器安全组规则 1581 1、拉取镜像 [rootTseng ~]# docker pull jenkins/jenkins:2.346.3 2.346.3: Pulling from jenkins/jenkins 001c52e26ad5: Pull complete 6b8dd635df38: Pull complete 2ba4c74fd680: Pull complet…

分布式事物XA、BASE、TCC、SAGA、AT

分布式事务——Seata 一、Seata的架构&#xff1a; 1、什么是Seata&#xff1a; 它是一款分布式事务解决方案。官网查看&#xff1a;Seata 2.执行过程 在分布式事务中&#xff0c;会有一个入口方法去调用各个微服务&#xff0c;每一个微服务都有一个分支事务&#xff0c;因…

分布式日志系统设计

一、分布式日志系统定义 分布式日志系统是一种用于收集、存储和分析大规模分布式系统日志的系统。它可以帮助开发人员和系统管理员实时监控和调试系统&#xff0c;提高系统可靠性和可用性&#xff0c;同时也可以用于日志分析和故障排查。 二、简单设计思路 日志收集&#xff…

VSCode设置字体

参考文章&#xff1a;【面向小白】vscode最佳实践&#xff08;2&#xff09;—— 字体设置&#xff08;fira code更纱黑体&#xff09;&#xff0c;这篇文章末尾给了安装字体的链接。 配置的字体还是很好看的。 ‘Fira Code Retina’, ‘Sarasa Mono Sc’ 需要注意的一个点&am…

leaflet 双屏对比

本章主要讲的是leaflet的双屏对比&#xff0c;本文参考了插件&#xff1a;Leaflet.Sync&#xff0c;我这里对原有的文件进行了重写&#xff0c;去掉了一部分不需要的内容&#xff0c;增加了flyTo和panTo方法&#xff0c;新的方法&#xff0c;如果需要可以自行下载资源。 目录 …

什么是语义空间?

一、概念 今天我们来聊一聊一个比较抽象的概念——语义空间。语义空间&#xff08;Semantic Space&#xff09;是自然语言处理&#xff08;NLP&#xff09;领域中一个重要的概念&#xff0c;在语义空间中&#xff0c;文本中的语义信息通过数学的方式来表示和处理。语义空间是一…

Linux系统nginx版本升级

最近公司漏扫有涉及到需要升级nginx的部分, 以下是一些总结经验 检查当前nginx版本 执行命令: nginx -V 如果没有设置环境变量则需要进入到nginx目录sbin目录下执行: ./nginx -V 下载最新版nginx nginx下载地址: https://nginx.org/en/download.html 1)选择稳定版本 wget h…

如何高效的向AI大模型提问? - 提示工程Prompt Engineering

大模型的输入&#xff0c;决定了大模型的输出&#xff0c;所以一个符合要求的提问Prompt起到关键作用。 以下是关于提示工程Prompt Engineering主要方法的详细表格&#xff0c;包括每种方法的优点、缺点、应用场景以及具体示例&#xff1a; 主要方法优点缺点应用场景示例明确性…

QT 多级嵌套结构体,遍历成员--半自动。<模板+宏定义>QTreeWidget树结构显示

Qt的QTreeWidget来显示嵌套结构体的成员&#xff0c;并以树形结构展示。 #include <QApplication> #include <QTreeWidget> #include <QTreeWidgetItem> #include <QString> #include <cstdint>// 假设这些是你的结构体定义 struct BaseMeterPa…

鸿蒙实现数据管理

目录&#xff1a; 1、鸿蒙实现数据管理的三种方式2、用户首选项3、键值型数据管理3.1、获取KVManager实例&#xff0c;用于管理数据库对象3.2、创建并获取键值数据库3.3、调用put()方法向键值数据库中插入数据3.4、调用get()方法获取指定键的值3.5、调用delete()方法删除指定键…

linux 下硬盘挂载c

1. 检查硬盘的文件系统类型 确保你所尝试挂载的硬盘 /dev/vdb 上已经有一个有效的文件系统。你可以用 lsblk -f 令查看硬盘的文件系统类型。 lsblk -f2. 检查挂载命令的语法 硬盘已经格式化为 ext4 sudo mount -t ext4 /dev/vdb /data 确保你在挂载时没有指定错误的文件系统…

1.网络知识-IP与子网掩码的关系及计算实例

IP与子网掩码 说实话&#xff0c;之前没有注意过&#xff0c;今天我打开自己的办公地电脑&#xff0c;看到我的网络配置如下&#xff1a; 我看到我的子网掩码是255.255.254.0&#xff0c;我就奇怪了&#xff0c;我经常见到的子网掩码都是255.255.255.0啊&#xff1f;难道公司配…

learn-(Uni-app)输入框u-search父子组件与input输入框(防抖与搜索触发)

1.父子组件u-search &#xff08;1&#xff09;父组件 <!-- 父组件 --> <template> <div><searchBar change"change" search"search"></searchBar> </div> </template> <script> // 子组件搜索 import…

基于PHP课堂签到系统的设计与实现

摘 要 随着教育业的迅速发展和学生人数的不断增加&#xff0c;导致在班级登记制度中传统的“点到”方式不能适应学校的实际需要。从而需要设计一个好的课堂签到系统将会对课堂签到管理工作带来事半功倍的效果。文章着重介绍了基于实践应用的班级签到系统的开发流程&#xff0c…

Qt Designer,仿作一个ui界面的练习(一):界面的基本布局

初学不要太复杂&#xff0c;先做一个结构简单的&#xff0c;大致规划一下功能分区&#xff0c;绘制草图&#xff1a; 最终的效果&#xff1a; 界面主要由顶边栏、侧边栏、内容区构成。顶边栏左边是logo&#xff0c;右边是时钟显示。侧边栏最上边是切换按钮&#xff0c;用以动画…

NES游戏机项目制作笔记(未完成)

24年12月1日晚记——在网上找项目学习的时候发现一个有意思的项目&#xff0c;准备靠这个应用一些STM32的高级功能。值得提醒的是——目的在于学习不可贪杯&#xff0c;注意效率 01 根据项目需求分析 为确保充分考虑每一个细节&#xff0c;并且让自己高效的完成项目制作&#…

C#开发-集合使用和技巧(十)Union用法-并集

在 C# 中&#xff0c;IEnumerable 的 Union 方法用于返回两个序列的并集。Union 方法会去除重复的元素&#xff0c;确保结果集中每个元素都是唯一的。以下是 Union 方法的基本用法&#xff1a; 基本语法 public static IEnumerable<TSource> Union<TSource>(this…

Unity集成Wwise并进行开发

1. 背景 项目要接入WWise&#xff0c;学习一下 1.1 与Unity自带音频系统的区别 Unity有自己的原生音乐功能&#xff1a;AduioSound。但是这个功能较为简单&#xff0c;对于音效开发人员来说并不是很友好。在一些大型的游戏中&#xff0c;音效会接入Wwise这个软件。音效开发者…

如何绕过IP禁令

网站、游戏和应用程序可以屏蔽特定IP地址&#xff0c;从而阻止使用该IP地址的任何人访问其服务。这称为IP禁令。管理员可以出于多种原因&#xff08;例如发出过多请求或可疑活动&#xff09;屏蔽IP地址。但是&#xff0c;这些禁令会使收集数据或访问在线内容变得更加困难。 一…