C# 委托详解(Delegate)

news2024/9/24 23:22:52

引言

在 C# 编程当中,委托(Delegate)是一种特殊的类型,它允许将方法作为参数传递给其他方法,或者将方法作为返回值返回,这种特性使得委托成为实现回调函数、事件处理等,所有的委托都派生自System.Delegate类。

C#中的委托类似于C 或 C++中函数的指针。委托是存在对某个方法的引用的一种引用类型变量,引用可以在运行的时候被打断,当事件出现,就必然有委托的影子,而有委托的时候却不一定会有事件出现。


委托的概念

委托是一种引用类型变量,存储某一个方法的内存地址

委托的格式

public delegate 返回值类型 MyDelegate();
//这里面的Mydelegate就是一个委托类型

委托的使用步骤

  1. 声明一个委托(函数指针)
  2. 创建委托对象
  3. 创造符合委托格式的函数。(指针指向的函数)
  4. 将函数名称赋值给委托。

这里举个例子吧:

using System;
namespace 练习
{
    internal class Program
    {
        public delegate int Count(int a, int b);

        public static int Add(int a, int b)
        {
            return a + b;
        }
        static void Main(string[] args)
        {
            Count c1 = new Count(Add);
            int c = c1(1, 2);
            Console.WriteLine(c);
            Console.Read();
        }
    }
}

在这串代码里面 定义了一个名为 "Count"委托类型,这里 Count 可以引用任何接收两个int参数并且返回一个 int 的方法

定义了一个名为 "Add" 的静态方法,它接受了两个 int 参数并且返回它们的和

在Main方法里面,创建了一个Count类型的委托实例 c1 ,并将其初始化引用为 Add 方法,并传入了这两个参数

调用委托 c1,传入两个参数1和2,由于委托引用 Add 方法,所以这行代码实际上调用了Add方法,并传入了这两个参数

小贴士

        使用委托时,我们一般采用两种情况

  1. 直接调用委托变量,就像刚才例子里面的那样
  2. 使用 invoke() ,简单就直接上代码例子:

委托的用处(配有代码例子)

  • 回调:

委托是实现回调的基础,它允许程序将一个方法(或函数)作为参数传递给另一个方法,然后在某个事件发生时,由接收方法调用这个方法

using System;

namespace 练习
{
    // 定义一个名为Button的类,用于模拟一个按钮的行为
    class Button
    {
        // 定义一个委托类型MyButton
        public delegate void MyButton();

        // ClickButton是一个委托类型的成员变量,用于存储按钮点击时的回调方法
        public MyButton ClickButton;

        public void Click()
        {
            Console.WriteLine("准备点击按钮");
            if (ClickButton != null)
            {
                ClickButton();
            }
        }
    }

    internal class Program
    {
        static void MyClick()
        {
            Console.WriteLine("按钮被点击了!");
        }

        static void Main(string[] args)
        {
            // 创建Button类的一个实例
            Button button = new Button();            
            // 将ClickButton委托设置为引用MyClick方法,这样点击按钮时会调用MyClick
            button.ClickButton = new Button.MyButton(MyClick);           
            // 模拟点击按钮,这将触发ClickButton委托,调用MyClick方法
            button.Click();         
            Console.Read();
        }
    }
}
  • 事件处理

事件是基于委托实现的。事件允许对象在发生特定行为时通知其他对象,而委托提供了一种机制来注册和触发这些通知

  • 多播委托

即一个委托可以引用多个方法。这使得你可以将多个方法链接在一起,以便它们可以依次或同时被调用。委托对象可使用 "+" 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。"-" 运算符可用于从合并的委托中移除组件委托。

using System;

namespace 委托的多播
{
    internal class Program
    {
        // 定义一个委托类型CalculateDelegate,它可以引用接受两个int参数并返回int的方法
        public delegate int CalculateDelegate(int x, int y);

        // Add方法实现了CalculateDelegate委托的签名,用于加法运算
        public static int Add(int x, int y)
        {
            // 输出加法运算的结果
            Console.WriteLine(x + y);
            return x + y;
        }

        // Multiply方法实现了CalculateDelegate委托的签名,用于乘法运算
        public static int Multiply(int x, int y)
        {
            // 输出乘法运算的结果
            Console.WriteLine(x * y);
            return x * y;
        }

        static void Main(string[] args)
        {
            // 创建CalculateDelegate委托的实例cal0,引用Add方法
            CalculateDelegate cal0 = new CalculateDelegate(Add);
            // 创建CalculateDelegate委托的实例cal1,引用Multiply方法
            CalculateDelegate cal1 = new CalculateDelegate(Multiply);
            // 通过委托的加法运算创建一个多播委托cal,它将依次执行cal0和cal1引用的方法
            CalculateDelegate cal = cal0 + cal1;
            // 调用多播委托cal,传入1和3作为参数
            // 这将依次调用Add和Multiply方法,并输出结果
            cal(1, 3);
            Console.ReadKey();
        }
    }
}
  • 泛型委托

以引用具有泛型参数的方法,从而提供类型安全和灵活性

using System;

// 定义一个泛型委托
public delegate R MyFunc<T, R>(T t);

class Program
{
    // 一个接受整数并返回字符串的方法
    static string ConvertToString(int number)
    {
        return number.ToString();
    }
    static void Main()
    {
        // 创建泛型委托的实例,引用一个方法
        MyFunc<int, string> toString = ConvertToString;

        // 使用委托调用方法
        string result = toString(123);
        Console.WriteLine(result); // 输出 "123"

        // 可以引用不同类型的方法
        MyFunc<double, int> round = Math.Round;
        int roundedResult = round(3.14159); // 调用 Math.Round 方法
        Console.WriteLine(roundedResult); // 输出 "3"
    }
}
  • 法的抽象表示

委托允许你将方法视为一个对象,这使得方法可以像其他对象一样被传递、存储和操作

using System;

namespace MethodAbstractionExample
{
    // 定义一个委托类型,它抽象表示一个方法,该方法接受两个int参数并返回一个int结果
    public delegate int Operation(int x, int y);

    class Program
    {
        // 这是一个具体的加法方法,符合委托的签名
        public static int Add(int a, int b)
        {
            return a + b;
        }

        // 这是一个具体的减法方法,也符合委托的签名
        public static int Subtract(int a, int b)
        {
            return a - b;
        }

        static void Main(string[] args)
        {
            // 创建委托实例,将加法方法作为抽象方法引用
            Operation operation = new Operation(Add);
            int result = operation(5, 3); // 调用抽象方法,实际执行加法
            Console.WriteLine($"结果是: {result}"); // 输出 8

            // 可以改变委托引用的方法,以抽象表示不同的方法
            operation = new Operation(Subtract);
            result = operation(5, 3); // 这次实际执行的是减法
            Console.WriteLine($"结果是: {result}"); // 输出 2

            // 等待用户输入,以便查看控制台输出
            Console.ReadLine();
        }
    }
}
  • 匿名方法和 Lambda 表达式

public delegate void MyDelegate(string message);

public class Program
{
    public static void Main()
    {
        MyDelegate del = delegate (string msg)
        {
            Console.WriteLine(msg);
        };
        
        del("!");
    }
}
public delegate void MyDelegate(string message);

public class Program
{
    public static void Main()
    {
        MyDelegate del = (msg) => Console.WriteLine(msg);
        
        del("!");
    }
}

为什么使用委托

委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。与 C 或 C++ 中的函数指针不同,委托是类型安全的,并且是面向对象的。委托不仅能够实现代码的重用,还能促进逻辑的解耦,从而提高代码的可维护性和可扩展性。


其他委托形式

Action

  • Action 表示无参数且无返回值的委托。
  • Action<int, string> 是一个带有参数且无返回值的泛型委托,最多支持16个参数。
  • Action 可以涵盖所有无返回值的委托,是对无返回值委托的一个统一封装。
using System;

public class Program
{
    static void DisplayMessage()
    {
        Console.WriteLine("Hello, World!");
    }

    static void DisplayMessage(string message)
    {
        Console.WriteLine(message);
    }

    public static void Main()
    {
        // 无参数 Action
        Action actionWithoutParams = DisplayMessage;
        actionWithoutParams(); // 输出 "Hello, World!"

        // 带参数 Action
        Action<string> actionWithParams = DisplayMessage;
        actionWithParams("Hello!"); // 输出 "Hello!"
    }
}

Func

  • Func 是一种表示有返回值的委托,它必须返回一个值。
  • Func 可以无参数,也可以接受最多16个参数,其中最后一个参数指定返回值的类型。
  • Func 的使用方式与 delegate 相同,但它专门用于处理有返回值的情况,为这些委托提供了一个统一的封装。
using System;

public class Program
{
    // Func 示例,接受两个int参数,返回它们的和
    static int Sum(int x, int y)
    {
        return x + y;
    }

    public static void Main()
    {
        // 创建 Func 委托实例
        Func<int, int, int> sumFunc = Sum;

        // 调用 Func 委托
        int result = sumFunc(5, 10); // 返回 15
        Console.WriteLine($"result: {result}");
    }
}

 Predicate<T>

用于表示接受一个类型为 T 的参数并返回 bool 的方法。我们展示了如何使用筛选数组中的偶数

using System;
using System.Linq;

public class Program
{
    public static void Main()
    {
        // 创建一个整数数组
        int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        // 使用 Predicate<T> 来筛选数组中的偶数
        Predicate<int> isEven = number => number % 2 == 0;
        // 筛选出所有偶数
        int[] evenNumbers = numbers.Where(isEven).ToArray();
        // 输出筛选结果
        Console.WriteLine("numbers:");
        foreach (var number in evenNumbers)
        {
            Console.WriteLine(number);
        }
    }
}

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

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

相关文章

【STM32开发笔记】使用RT-Thread的SDIO驱动和FATFS实现SD卡文件读写

【STM32开发笔记】使用RT-Thread的SDIO驱动和FATFS实现SD卡文件读写 一、准备工作1.1 准备好开发板和SD卡1.2 创建RT-Thread项目 二、配置RT-Thread2.1 打开文件系统相关配置2.2 打开SD卡相关配置2.3 打开RTC配置2.4 重新生成Keil项目文件 三、编译、烧录、运行3.1 编译项目3.2…

网站建设完成后, 营销型网站如何做seo

营销型网站的SEO优化旨在提高网站在搜索引擎中的排名&#xff0c;从而吸引更多潜在客户并促进销售。以下是营销型网站SEO的详细解析&#xff1a; 关键词研究与优化 目标受众分析&#xff1a;了解目标受众的搜索习惯和需求&#xff0c;确定适合的关键词。使用工具来发现相关关键…

RV1126的GPIO计算和使用

1、获取GPIO芯片对应的序号值 先读取下/sys/kernel/debug/gpio的值&#xff0c;得到每个GPIO芯片的序号范围&#xff0c;如GPIO芯片0就为0~31。 2、根据GPIO硬件编号计算出系统内使用的GPIO序号 根据GPIO的编号&#xff0c;比如说GPIO3_B0&#xff0c;前面GPIO3代表看GPIO3的信…

傻瓜操作:GraphRAG、Ollama 本地部署及踩坑记录

目录 一、GraphRAG 介绍1.引言2.创新点3. 算法4. 数据和实验结果5.不足和展望 二、本地部署1.为什么要本地部署2.环境准备3. GraphRAG 安装3.1 下载 GraphGAG3.2 安装依赖包3.3 创建数据目录3.4 项目初始化3.5 修改配置文件 3.6 修改.env文件3.7 修改源码 4. Indexing5. query5…

Linux关于压缩之后文件更大的解释

记录于24年八月29 使用vim命令创建了lianxi1和lianxi2并在里面填写了一些内容&#xff0c;发现使用gzip和zip压缩后文件反而更大 事后问了一下ai回答了我的疑惑 压缩算法开销&#xff1a;如前所述&#xff0c;压缩文件需要存储额外的元数据和文件结构信息。这种开销在处理非常…

C++ TinyWebServer项目总结(13. 多进程编程)

本章讨论Linux多进程编程的以下内容&#xff1a; 复制进程映像的fork系统调用和替换进程映像的exec系列系统调用。僵尸进程以及如何避免僵尸进程。进程间通信&#xff08;Inter Process Communication&#xff0c;IPC&#xff09;最简单的方式&#xff1a;管道。三种System V进…

浏览器插件利器--allWebPluginV2.0.0.18-alpha版发布

allWebPlugin简介 allWebPlugin中间件是一款为用户提供安全、可靠、便捷的浏览器插件服务的中间件产品&#xff0c;致力于将浏览器插件重新应用到所有浏览器。它将现有ActiveX控件直接嵌入浏览器&#xff0c;实现插件加载、界面显示、接口调用、事件回调等。支持Chrome、Firefo…

[MRCTF2020]Unravel!!

使用zsteg查看图片有隐藏文件&#xff0c;没有头绪&#xff0c;先放弃 使用zsteg和010editor查看都发现一个png图片 把JM.png拷贝到kali&#xff0c;使用binwalk分离&#xff0c;得到一个aes.png 使用010editor查看wav&#xff0c;发现尾部有可疑的字符串&#xff0c;拷贝出来备…

记一次应急响应之网站暗链排查

目录 前言 1. 从暗链而起的开端 1.1 暗链的介绍 1.2 暗链的分类 2. 在没有日志的情况下如何分析入侵 2.1 寻找指纹 2.2 搜索引擎搜索fofa资产搜索 2.2.1 fofa资产搜索 2.2.2 bing搜索引擎搜索 3.通过搭建系统并进行漏洞复现 4. 应急响应报告编写 前言 免责声明 博文…

二叉树(binary tree)遍历详解

一、简介 二叉树常见的遍历方式包括前序遍历、中序遍历、后序遍历和层序遍历等。我将以下述二叉树来讲解这几种遍历算法。 1、创建二叉树代码实现 class TreeNode:def __init__(self,data):self.datadataself.leftNoneself.rightNonedef createTree():treeRootTreeNode(F)N…

大模型提示词工程技术3-提示词输入与输出的优化的技巧详细介绍

大模型提示词工程技术3-提示词输入与输出的优化的技巧详细介绍。《大模型提示词工程技术》的作者&#xff1a;微学AI&#xff0c;这是一本专注于提升人工智能大模型性能的著作&#xff0c;它深入浅出地讲解了如何通过优化输入提示词来引导大模型生成高质量、准确的输出。书中不…

腾讯地图三维模型加载GLTF,播放模型动画

腾讯地图三维模型加载&#xff0c;播放模型动画 关键代码 const clock new THREE.Clock();console.log(gltf)// 确保gltf对象包含scene和animations属性if (gltf && gltf.scene && gltf.animations) {// 创建AnimationMixer实例&#xff0c;传入模型的scenec…

【51单片机】2-3-1 【I/O口】【电动车防盗报警项目】震动传感器实验1—震动点灯

1.硬件 51单片机最小系统LED灯模块震动传感器模块 2.软件 main.c程序 #include "reg52.h"sbit led1 P3^7;//根据原理图&#xff08;电路图&#xff09;&#xff0c;设备变量led1指向P3组IO口的第7口 sbit vibrate P3^3;//Do接到了P3.3口void Delay2000ms() //…

力扣刷题--2185. 统计包含给定前缀的字符串【简单】

题目描述 给你一个字符串数组 words 和一个字符串 pref 。 返回 words 中以 pref 作为 前缀 的字符串的数目。 字符串 s 的 前缀 就是 s 的任一前导连续字符串。 示例 1&#xff1a; 输入&#xff1a;words [“pay”,“attention”,“practice”,“attend”], pref “at…

用 Higress AI 网关降低 AI 调用成本 - 阿里云天池云原生编程挑战赛参赛攻略

作者介绍&#xff1a;杨贝宁&#xff0c;爱丁堡大学博士在读&#xff0c;研究方向为向量数据库 《Higress AI 网关挑战赛》正在火热进行中&#xff0c;Higress 社区邀请了目前位于排行榜 top5 的选手杨贝宁同学分享他的心得。下面是他整理的参赛攻略&#xff1a; 背景 我们…

Jmeter(十四)Jmeter分布式部署测试

单个接口测试&#xff0c;我们使用谷歌的插件postman 多个接口测试&#xff0c;我们使用Jmeter进行测试 一、使用工具测试 1、使用Jmeter对接口测试 首先我们说一下为什么用Posman测试后我们还要用Jmeter做接口测试&#xff0c;在用posman测试时候会发现的是一个接口一个接…

存储实验:基于华为存储实现存储双活(HyperMetro特性)

目录 什么是存储双活仲裁机制 实验需求实验拓扑实验环境实验步骤1. 双活存储存储初始化&#xff08;OceanStor v3 模拟器&#xff09;1.1开机&#xff0c;设置密码1.2登录DM&#xff0c;修改设备名、系统时间和导入License1.3 设置接口IP 2. 仲裁服务器配置&#xff08;Centos7…

C++ 两线交点程序(Program for Point of Intersection of Two Lines)

示例图 给定对应于线 AB 的点 A 和 B 以及对应于线 PQ 的点 P 和 Q&#xff0c;找到这些线的交点。这些点在 2D 平面中给出&#xff0c;并带有其 X 和 Y 坐标。示例&#xff1a; 输入&#xff1a;A (1, 1), B (4, 4) C (1, 8), D (2, 4) 输出&#xff1a;给定直…

关于vue2运行时filemanager-webpack-plugin报错isFile is undefind

当我们在运行时报此错误时&#xff0c;在vue.config.js里找一下filemanager-webpack-plugin的配置路径。 new FileManagerPlugin({onEnd: {delete: [./dist.zip],archive: [{ source: ./dist, destination: ./dist.zip }]}}) 在对应的路径下建一个dist文件夹

scrapy--子类CrawlSpider中间件

免责声明:本文仅做分享参考~ 目录 CrawlSpider 介绍 xj.py 中间件 部分middlewares.py wyxw.py 完整的middlewares.py CrawlSpider 介绍 CrawlSpider类&#xff1a;定义了一些规则来做数据爬取&#xff0c;从爬取的网页中获取链接并进行继续爬取. 创建方式&#xff…