C# 委托简述

1.委托

    1.1什么是委托

委托委托        官网解释: 委托是安全封装方法的类型,类似于 C 和 C++ 中的函数指针。 与 C 函数指针不同的是,委托是面向对象的、类型安全的和可靠的。 委托的类型由委托的名称确定。

        个人理解:委托就是一个方法的模板。它可以接收签名(签名一致就是方法的参数与返回值类型与其一致)与它一致的方法作为参数传递给它执行。

     1.2 如何定义委托

                使用关键字 delegate 声明委托

public delegate int GetNum(int a, int b);

                除了使用delegate 定义 委托以外C#提供两个定义好的泛型委托 (ActionFunc) (当然你也可以使用delegate自定义泛型委托)

这里先介绍两个C#定义的委托,相信很多人都在使用,不管你清不清楚什么是委托

Action:是无返回值的泛型委托。

下面action 表示无参,无返回值的委托

private void WTBtn_Click(object sender, EventArgs e)
{Action action = new Action(AA);//定义委托action();//使用方式1action.Invoke();//使用方式2
}private void AA()
{Console.WriteLine("我是传入的无参无返回值的委托方法");
}

下面action 表示有传入参数int,string无返回值的委托

private void WTBtn_Click(object sender, EventArgs e)
{Action<int,string> action = new Action<int, string>(AA);action(1,"");//使用方式1action.Invoke(1, "");//使用方式2
}private void AA(int a,string b)
{Console.WriteLine("我是传入的有参无返回值的委托方法");
}

Func:是有返回值的泛型委托。Func<object,string,intobject,string为参数。int为返回值

下面action 表示有传入参数int返回值string的委托

private void WTBtn_Click(object sender, EventArgs e)
{Func<int,string> action = new Func<int, string>(AA);//a=b="我是传入的有参无返回值的委托方法"var a =  action(1);//使用方式1var b= action.Invoke(1);//使用方式2
}private string AA(int a)
{Console.WriteLine("我是传入的有参无返回值的委托方法");return "我是传入的有参无返回值的委托方法";
}

        运行结果如下图:

下面action 表示有无参数有返回值string的委托

private void WTBtn_Click(object sender, EventArgs e)
{Func<string> action = new Func<string>(AA);//a=b="我是传入的有参无返回值的委托方法"var a =  action();//使用方式1var b= action.Invoke();//使用方式2
}private string AA()
{Console.WriteLine("我是传入的有参无返回值的委托方法");return "我是传入的有参无返回值的委托方法";
}

 C#自带的委托说完了,下面说 delegate 自定义的委托

下面代码定义了一个委托和一个静态类的方法。委托的级别与类等同,所以可以定义在命名空间里,当然也能定义在类里。

 /// <summary>/// 定义委托/// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <returns></returns>public delegate int GetNum(int a, int b);public static class WTModel{/// <summary>/// 加法/// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <returns></returns>public static int Add(int a,int b){Console.WriteLine("执行Add了方法");return a + b;}/// <summary>/// 减法/// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <returns></returns>public static int Sub(int a, int b){Console.WriteLine("执行Sub了方法");return a - b;}/// <summary>/// 乘法/// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <returns></returns>public static int Multi(int a, int b){Console.WriteLine("执行Multi了方法");return a * b;}/// <summary>/// 除法/// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <returns></returns>public static int Division(int a, int b){Console.WriteLine("执行Division了方法");if (b == 0){return 0;}return a / b;}}

定义的GetNum委托与C#自带的两个委托是同级的,使用方式也是一页,只不C#自带的是泛型的,GetNum却是固定了两个参数为int类型且返回值是int类型的签名。故此能传入GetNum的方法的参数与返回值必须与GetNum保持一致

使用方式:下面将上面Add方法传入委托并执行。

private void WTBtn_Click(object sender, EventArgs e)
{//实例化方式1GetNum num = new GetNum(WTModel.Add);//使用方法1var a = num(1, 2);//使用方法2var b = num.Invoke(1, 2);
}

执行结果:

值得注意的是,上述的委托执行方式都是同步的,并不是说把方法给到委托就会自动开启新的线程。看下面执行例子:

private void WTBtn_Click(object sender, EventArgs e)
{//实例化方式1GetNum num = new GetNum(WTModel.Add);//使用方法1var a = num(1, 2);//使用方法2var b = num.Invoke(1, 2);Console.WriteLine("我是一定是最后执行的");}

 执行结果可初步看出是在委托执行了两次Add方法调用后才打印最后一段文字的(当然此处并不严谨,用于说明同步足够了):

那么如何异步执行呢 ,这里就不得不提BeginInvoke了 ,如下:

 private void WTBtn_Click(object sender, EventArgs e){//实例化方式1GetNum num = new GetNum(WTModel.Add);//使用方法1var a = num(1, 2);//使用方法2//倒数第二个参数为回调函数,暂用null,//最后一个参数是给回调函数传入参数的参数,暂用null//IAsyncResult result = num.BeginInvoke(1, 2, null, null);var b = num.BeginInvoke(1,2,null,null);Console.WriteLine("我是不一定是最后执行的了");}

运行结果你看第二次调用Add就不一样了吧:

BeginInvoke在NetCore不支持,NetFamework支持 NetCore有更好的多线程功能来支持实现类似功能。所以在NET Core中需要自己手动去开启线程和实现异步。例如下面(在Famework里也可以这样使用异步):

 /// <summary>/// 定义委托/// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <returns></returns>public delegate Task<int> GetNum(int a, int b);public static class WTModel{/// <summary>/// 加法/// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <returns></returns>public static Task<int> Add(int a, int b){return Task.Run(() =>{Console.WriteLine("执行Add了方法");return a + b;});}
}
 private void WTBtn_Click(object sender, EventArgs e){//实例化方式1GetNum num = new GetNum(WTModel.Add);//使用方法1var a = num(1, 2);//使用方法2var b = num.Invoke(1,2);Console.WriteLine("我是不一定是最后执行的了");}

执行结果:

1.3:多播委托

所谓多播委托就是给委托传了多个方法,执行委托的时候会按照传入的顺序依次执行方法,当然传入的方法要注意错误处理,否则中件某个方法异常会导致后续方法无法执行,若委托有返回值,返回的值是最后一个方法的返回值。

定义多播(+=或者-=):

 private void WTBtn_Click(object sender, EventArgs e){//多播委托//实例化方式1GetNum num = new GetNum(WTModel.Add);GetNum num1 = WTModel.Multi;//实例化方式2num += num1;num += WTModel.Sub;var c = num(1, 2);//调用一次会依次执行Add、Multi、Sub方法最终返回的值是最后一个委托的方法的返回值。}

 

1.4 实际应用 

说了这么多,委托到底好在哪里呢,很多人都有这个疑问吧。下面举个例子就知道了 。

仍以上面定义的GetNum委托和其四个方法为基础。

正常我们要算加法直接使用Add方法不久一步到位了吗,为何还需要多此一举定义个委托来完成调用呢,这应该是很多看到这里的人的疑惑。

场景一:委托作为参数执行不同的方法,这样在WTBtn_Click方法中就只用调用Use1就能计算不同的值:

private void WTBtn_Click(object sender, EventArgs e)
{Use1(1, 2, WTModel.Add);Use1(1, 2, WTModel.Sub);Use1(1, 2, WTModel.Multi);Use1(1, 2, WTModel.Division);
}private void Use1(int a, int b, GetNum num)
{var A = num(1, 2);
}

场景2:实现根据不同地方输出产出的水果是啥

方法1:if或者switch以switch为例:

private string Use(string name)
{string a = "";switch (name){case "广州": a = "苹果"; break;case "重庆": a = "香蕉"; break;case "四川": a = "梨"; break;case "广西": a = "西瓜"; break;default: break;}return a;
}

 使用:

Console.WriteLine(Use("广州"));

方法2:定义四个方法,分别调用方法

public static class WTModel
{public static void GZ(string name){if (name == "广州"){Console.WriteLine("苹果");}}public static void CQ(string name){if (name == "重庆"){Console.WriteLine("香蕉");}}public static void SC(string name){if (name == "四川"){Console.WriteLine("梨");}}public static void GX(string name){if (name == "广西"){Console.WriteLine("西瓜");}}
}

使用:

private void WTBtn_Click(object sender, EventArgs e)
{string name = "广州";WTModel.GZ(name);WTModel.GX(name);WTModel.CQ(name);WTModel.SC(name);}

方法3、使用委托:

定义委托和其四个方法

 /// <summary>/// 定义委托/// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <returns></returns>public delegate void GetName(string name);public static class WTModel{public static void GZ(string name){if (name == "广州"){Console.WriteLine("苹果");}}public static void CQ(string name){if (name == "重庆"){Console.WriteLine("香蕉");}}public static void SC(string name){if (name == "四川"){Console.WriteLine("梨");}}public static void GX(string name){if (name == "广西"){Console.WriteLine("西瓜");}}}

使用委托:

private void WTBtn_Click(object sender, EventArgs e)
{GetName get = WTModel.GZ;get += WTModel.GX;get += WTModel.CQ;get += WTModel.SC;Use("广州", get);
}private void Use(string name, GetName get)
{get.Invoke(name);
}

三个方法都可以实现根据地方不同输出相应水果。调用结果都是如下;

从三个方法写法和调用来看似乎方法一最为简单。

那么,现在你的程序已经是开发好了,这个时候突然来了两个需求。

需求1:新增一个地方的水果:

方法一需要新增一行case代码 。

方法二需要新增一个方法。

方法三需要新增一个方法。

看起来仍是方法一简单,但是 如果你使用的是是打包好的DLL文件,那么你就没办法直接增加代码,除非能重写方法。

需求2:在输出水果前先输出产地。

方法一 需要编辑每行case代码 。

方法二 需要编辑每个方法。

方法三 仅需在Use方法里新增一行即可。

若你使用的是是打包好的DLL文件且方法无法被重写,那似乎仅方法三能满足需求。

1.5 事件

本来准备单独说的,但事件本质是基于委托实现的观察者模式的应用。贴两个觉得可以的链接自行去看吧。

事件的理论即使用注意:C# 事件(event)_c# event-CSDN博客

事件的应用理解:分分钟用上C#中的委托和事件 - 雾中人 - 博客园

结语:个人认为委托的主要作用在于实现代码的重用,业务的解耦,保持代码的稳定性以及扩展性。暂时就说这么多吧,仅个人理解。

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

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

相关文章

云渲染主要是分布式(分机)渲染,如何使用blender云渲染呢?

云渲染主要是分布式&#xff08;分机&#xff09;渲染&#xff0c;比如一个镜头同时开20-100张3090显卡的机器渲染&#xff0c;就能同时渲染20-100帧&#xff0c;渲染不仅不占用自己电脑&#xff0c;效率也将增加几十上百倍&#xff01; blender使用教程如下&#xff1a; 第一…

Ansible 的脚本 --- playbooks剧本

playbooks 本身由以下各部分组成 &#xff08;1&#xff09;Tasks&#xff1a;任务&#xff0c;即通过 task 调用 ansible 的模板将多个操作组织在一个 playbook 中运行 &#xff08;2&#xff09;Vars&#xff1a;变量 &#xff08;3&#xff09;Templates&#xff1a;模板 &a…

qt QMainWindow详解

一、概述 QMainWindow继承自QWidget&#xff0c;并提供了一个预定义的布局&#xff0c;将窗口分成了菜单栏、工具栏、状态栏和中央部件区域。这些区域共同构成了一个功能丰富的主窗口&#xff0c;使得应用程序的开发更加简单和高效。 二、QMainWindow的常用组件及功能 菜单栏&…

CSS的外边距合并规则

有时候&#xff0c;我们给组件设置了外边距&#xff0c;但是在实际运行可能和预期不符&#xff0c;这里有一个知识点叫外边距合并规则 比如我们这里有三个容器&#xff0c;A和B都设置了外边距10px&#xff0c;那他们在水平方向的间距是一个求和关系&#xff0c;一共是20px 但…

物联网之超声波测距模块、arduino、esp32

MENU 原理硬件电路设计软件程序设计 原理 超声波是一种频率高于20000Hz的声波&#xff0c;功率密度为p≥0.3W/cm&#xff0c;它的方向性好&#xff0c;反射能力强&#xff0c;易于获得较集中的声能。超声波用于许多不同的领域&#xff0c;比如检测物体和测量距离&#xff0c;清…

JAVA Maven 的安装与配置

一、下载地址 官方网站&#xff1a;Maven – Download Apache Maven 我这里是3.8.6版本 二、安装步骤 maven安装之前要先安装jdk&#xff0c;请确保你的系统已经安装了jdk环境。 1.将下载好的 Maven 进行解压 apache-maven-3.6.8-bin.zip 2.配置本地仓库:修改 conf/settin…

理解计算机系统_简述链接

前言 以<深入理解计算机系统>(以下称“本书”)内容为基础&#xff0c;对程序的整个过程进行梳理。本书内容对整个计算机系统做了系统性导引,每部分内容都是单独的一门课.学习深度根据自己需要来定 引入 源代码在机器层面,是以指令存在的.指令包含了指令码和操作…

vscode离线状态ssh连接不断输入密码登不上:配置commit_id

如题&#xff0c;vscode在一个离线服务器上&#xff0c;通过remote-ssh登录远程服务器&#xff0c;不断弹出密码框&#xff0c;总是进不去&#xff0c;后来了解到主要是不同vscode版本需要下载对应抑制commit-id的vscode-server-linux-x64.tar.gz包。 1&#xff09;vscode, 点…

html小游戏-飞机大战

敌机图片&#xff1a; 子弹图片&#xff1a; 我方飞机&#xff1a; 目录结构 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>飞机大战</title><style>* {margin: 0;padding: 0;}#ga…

WebView渲染异常导致闪退解决方案

背景&#xff1a; App主页面使用了大量WebView容器(10个以上)显示图表信息&#xff0c;最新发现bugly上面出现一些关于浏览器Native Crash&#xff0c;如下&#xff1a; 经排查&#xff0c;是WebView渲染失败导致Crash&#xff0c;可以通过webView.loadUrl("chrome://cra…

如何微调(Fine-tuning)大语言模型?

导读 本文介绍了微调的基本概念&#xff0c;以及如何对语言模型进行微调。 从 GPT3 到 ChatGPT、从GPT4 到 GitHub copilot的过程&#xff0c;微调在其中扮演了重要角色。什么是微调&#xff08;fine-tuning&#xff09;&#xff1f;微调能解决什么问题&#xff1f;什么是 Lo…

计算机网络:数据链路层 —— 以太网(Ethernet)

文章目录 局域网局域网的主要特征 以太网以太网的发展100BASE-T 以太网物理层标准 吉比特以太网载波延伸物理层标准 10吉比特以太网汇聚层交换机物理层标准 40/100吉比特以太网传输媒体 局域网 局域网&#xff08;Local Area Network, LAN&#xff09;是一种计算机网络&#x…

Newstar_week1_week2_wp

week1 wp crypto 一眼秒了 n费马分解再rsa flag&#xff1a; import libnum import gmpy2 from Crypto.Util.number import * p 9648423029010515676590551740010426534945737639235739800643989352039852507298491399561035009163427050370107570733633350911691280297…

PostgreSQL的学习心得和知识总结(一百五十六)|auto_explain — log execution plans of slow queries

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

python-PyQt项目实战案例:制作一个视频播放器

文章目录 1. 关键问题描述2. 通过OpenCV读取视频/打开摄像头抓取视频3. 通过PyQt 中的 QTimer定时器实现视频播放4. PyQt 视频播放器实现代码参考文献 1. 关键问题描述 在前面的文章中已经分享了pyqt制作图像处理工具的文章&#xff0c;也知道pyqt通过使用label控件显示图像的…

庆祝程序员节:聊一聊编程语言的演变

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

qt配置https请求

qt应用版本 windows 32位 先说下心理路程&#xff0c;你能遇到的我都遇到了&#xff0c;你能想到的我都想到了&#xff0c;怎么解决看这一篇就够了&#xff0c;从上午12点到晚上12点几乎没离开电脑&#xff08;除了吃饭&#xff09;&#xff0c;对于openssl这种用的时候无感&am…

idea 2023 创建 springboot 项目 LTS

idea 2023 创建 springboot 项目 LTS idea 版本 2023.3.8 参考 idea 阿里 建立 springboot 工程 方法 LTS https://blog.csdn.net/wowocpp/article/details/124692532 File ---- New ---- Project https://start.spring.io/ http://start.aliyun.com http://127.0.0.1:8080…

旺店通对接金蝶云星空销售出库接口细节

数据集成是确保各系统高效协同运作的关键环节。本案例将重点介绍如何通过轻易云数据集成平台&#xff0c;实现旺店通旗舰奇门与金蝶云星空之间的销售出库数据对接&#xff0c;具体方案为“销售出库对接&#xff0c;供应商发货-new”。 在本次集成过程中&#xff0c;我们利用了…

Angular 保姆级别教程高阶应用 - RxJs

RxJS 13.1.1 什么是 RxJS ? RxJS 是一个用于处理异步编程的 JavaScript 库&#xff0c;目标是使编写异步和基于回调的代码更容易。 13.1.2 为什么要学习 RxJS ? 就像 Angular 深度集成 TypeScript 一样&#xff0c;Angular 也深度集成了 RxJS。 服务、表单、事件、全局状…