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();//使用方式1
    action.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,"");//使用方式1
    action.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);//使用方式1
    var 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();//使用方式1
    var 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)
{

    //实例化方式1
    GetNum num = new GetNum(WTModel.Add);
    //使用方法1
    var a = num(1, 2);
    //使用方法2
    var b = num.Invoke(1, 2);
}

执行结果:

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

private void WTBtn_Click(object sender, EventArgs e)
{


    //实例化方式1
    GetNum num = new GetNum(WTModel.Add);
    //使用方法1
    var a = num(1, 2);
    //使用方法2
    var b = num.Invoke(1, 2);

    Console.WriteLine("我是一定是最后执行的");

}

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

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

 private void WTBtn_Click(object sender, EventArgs e)
 {


     //实例化方式1
     GetNum num = new GetNum(WTModel.Add);
     //使用方法1
     var 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)
 {


     //实例化方式1
     GetNum num = new GetNum(WTModel.Add);
     //使用方法1
     var a = num(1, 2);
     //使用方法2
     var b = num.Invoke(1,2);

     Console.WriteLine("我是不一定是最后执行的了");
 }

执行结果:

1.3:多播委托

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

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

 private void WTBtn_Click(object sender, EventArgs e)
 {
     //多播委托
     //实例化方式1
     GetNum num = new GetNum(WTModel.Add);
     GetNum num1 = WTModel.Multi;//实例化方式2
     num += 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#中的委托和事件 - 雾中人 - 博客园

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

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

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

相关文章

关于bp抓不到本地包

关于bp抓不到本地包 关于bp抓不到本地包 关于bp抓不到本地包 pikachu练习时&#xff0c;发现用bp抓本地&#xff08;127.0.0.1&#xff09;数据包时&#xff0c;竟然直接放行访问。 是因为系统默认127.0.0.1无法使用代理&#xff0c;因此bp才抓不到本地数据包&#xff0c;需要…

Python入门:Python如何强制终止程序(如何强制终止多线程程序)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 文章内容 📒📝 优雅的退出📝 强制的终止📝 应用场景对比🚀 优雅的退出🚀 强制的终止⚓️ 相关链接 ⚓️📖 介绍 📖 在开发过程中,有时候需要在满足一些条件的情况下让程序强制终止运行?今天我们将了解一下 Py…

Apifox「定时任务」进阶指南:监控、爬虫的自动化之旅

定时任务能干啥&#xff1f;&#xff1f;&#xff1f; 它能做的自动化操作实在太多了。先给大家列几个常见的&#xff0c;比如&#xff1a; 社交媒体动态监控&#xff1a;定时跟踪特定用户的动态&#xff0c;监控热门话题和趋势 数据采集与分析&#xff1a;定时爬取网站的文章…

机器学习学习笔记-20241018

继续跟着小土堆去学习机器学习 文章目录 Flatten1. Flatten 的作用2. 何时使用 Flatten3. PyTorch 中的 Flatten Sequentia优化器模型的保存与加载模型的完整训练 Flatten 在神经网络中&#xff0c;Flatten 操作是将高维的输入&#xff08;如二维图像或三维特征图&#xff09…

ArcGIS 10.8 安装教程

目录 一、ArcGIS10.8二、安装链接三、安装教程四、ArcGIS实战 &#xff08;一&#xff09;ArcGIS10.8 1. 概述 ArcGIS 10.8是由美国Esri公司开发的GIS平台&#xff0c;用于处理、分析、显示和管理地理数据&#xff0c;并实现数据共享。它具有新特性和功能&#xff0c;性能更…

C++ 设计模式 - 每日持续更新中

设计模式的核心 - 隔离程序的变化点和稳定点 零&#xff1a;面向对象设计八大原则 ①&#xff1a;依赖倒置(Dependency Inversion Principle) 高层模块不应依赖于低层模块&#xff0c;二者都应依赖于抽象&#xff1a;这意味着程序的高层逻辑不应该直接依赖于具体实现&#xf…

探索 SVG 创作新维度:svgwrite 库揭秘

文章目录 **探索 SVG 创作新维度&#xff1a;svgwrite 库揭秘**背景介绍库简介安装指南基础函数使用实战场景常见问题与解决方案总结 探索 SVG 创作新维度&#xff1a;svgwrite 库揭秘 背景介绍 在数字艺术和网页设计领域&#xff0c;SVG&#xff08;Scalable Vector Graphic…

QT:MaintenanceTool 模块安装工具

QT的MaintenanceTool 工具对已安装的 Qt 进行卸载、修复等其他操作时提示At least one valid and enabled repository required for this action to succeed 解决方式&#xff1a;在设置中添加一个临时的仓库 https://mirrors.tuna.tsinghua.edu.cn/qt/online/qtsdkrepositor…

Serv00 免费虚拟主机 零成本搭建 PHP / Node.js 网站

本文首发于只抄博客&#xff0c;欢迎点击原文链接了解更多内容。 前言 Serv00 是一个提供免费虚拟主机的平台&#xff0c;包含了 3GB 的存储空间和 512MB 的内存空间&#xff0c;足够我们搭建一个 1IP 的小网站了。同时他还不限制每月的流量&#xff0c;并提供了 16 个数据库&…

【深度学习代码调试5】标准化数据集:TensorFlow Datasets (TFDS)自动化数据加载与预处理

【标准化数据集】TensorFlow Datasets、TFDS&#xff1a;自动化数据加载与预处理 写在最前面1. 什么是 TensorFlow Datasets (TFDS)?主要特点&#xff1a; 2. TFDS 的核心 API&#xff1a;tfds.builder 和 download_and_preparetfds.builder&#xff1a;创建数据集构建器示例&…

FPGA实现PCIE视频采集转USB3.0输出,基于XDMA+FT601架构,提供3套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐本博已有的PCIE方案本博已有的USB通信方案 3、PCIE基础知识扫描4、工程详细设计方案工程设计原理框图电脑端视频PCIE视频采集QT上位机XDMA配置及使用XDMA中断模块FDMA图像缓存FT601功能和硬件电路FT601读时序解读FT601写时序解读U…

【源码+文档】基于JavaWeb的村民健康管理平台【提供源码+答辩PPT+参考文档+项目部署】

作者简介&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流。✌ 主要内容&#xff1a;&#x1f31f;Java项目、Python项目、前端项目、PHP、ASP.NET、人工智能…

未来医疗:大语言模型如何改变临床实践、研究和教育|文献精析·24-10-23

小罗碎碎念 这篇文章探讨了大型语言模型在医学领域的潜在应用和挑战&#xff0c;并讨论了它们在临床实践、医学研究和医学教育中的未来发展。 姓名单位名称&#xff08;中文&#xff09;Jan Clusmann德国德累斯顿工业大学埃尔朗根弗雷斯尼乌斯中心数字化健康研究所Jakob Nikola…

光纤传感器比传统传感器强在哪?——以大坝监测为例

应用介绍 大坝安全监测中心经常对当前工程中的大坝进行检查, 以确保水电站的安全运行。 大坝原有的观测模式是传感器加上人工观测模式&#xff0c;多数传感器经过多年运行后逐渐老化&#xff0c;出现测点损伤&#xff0c;且精度无法与现有光纤传输传感器相比&#xff0c;受现…

Django+Vue全栈开发项目入门(一)

Vue项目搭建过程 1、使用脚手架工具搭建项目 2、准备静态资源 3、调整生成项目结构 使用脚手架工具搭建项目 网络请求库axios Axios是一个基于Promise的HTTP库&#xff0c;适用于浏览器和node.js环境&#xff0c;用于发送网络请求。 特点 跨平台性&#xff1a;Axios既可…

Unity Apple Vision Pro 保姆级开发教程-环境配置、导入 PolySpatial 案例、程序发布到设备

视频教程 Unity 环境配置、导入 PolySpatial 案例、程序发布到设备 Unity Vision Pro 中文课堂教程地址&#xff1a; Unity3D Vision Pro 开发教程【保姆级】 | Unity 中文课堂 教程说明 这期教程我将介绍使用 Unity 开发 Apple Vision Pro 应用所需要的 Unity 环境配置&…

python实现投影仪自动对焦

这是一款投影仪,它带有对焦摄像头 它是如何自动对焦的呢? 我们先看一下对焦算法展示效果 说明:左侧是原视频,右侧是对调焦后的视频帧展示,如果下一帧视频比当前帧清晰就会显示下一帧,否则,还是显示当前帧,直至找到更清晰的帧 原理说明: 在投影仪上对焦摄像头就会实…

CDP和数据仓库怎么选?

一、CDP 是什么&#xff1f; 1.定义&#xff1a; CDP 全称是Customer Data Platform&#xff08;客户数据平台&#xff09;。是一种营销技术工具&#xff0c;它能够将来自不同渠道和系统&#xff08;如网站、移动应用、客户服务系统、营销自动化平台、社交媒体等&#xff09;…

C#PropertyGrid下拉选择数据报错

1、问题点--PropertyGrid下拉框报错 PropertyGrid&#xff1a;属性窗口&#xff1a;滚轮选择或者手动输入不报错&#xff0c;下拉框选择报错 属性值无效&#xff1a;类型“System:String”的对象无法转化为类型“System:Int32” PropertyGrid&#xff1a;属性窗口&#xff1a;…

论文笔记:SIBO: A Simple Booster for Parameter-Efficient Fine-Tuning

ACL 2024 1 intro 基于 Transformer 的大模型一般都有很多层 在广泛采用的 PEFT 技术&#xff08;包括 Adapters 和 LoRA&#xff09;中&#xff0c;尤其是在深层中&#xff0c;也存在过度平滑现象&#xff08;即token之间的相似度很高&#xff09;论文评估了同一语句中 toke…