聊聊 C# 中的委托

news2025/1/7 13:41:14

聊聊 C# 中的委托

      • 什么是委托(Delegate)
      • 单播委托(Unicast Delegate)
      • 多播委托(Multicast Delegate)
      • 内置委托(Action & Func)
        • 单播委托(使用 Action 和 Func)
        • 多播委托(使用 Action)
        • 多播委托(使用 Func)
      • 使用内置 Action 和 Func 的好处
        • Action 委托
        • Func 委托
      • 什么情况下才需要自定义委托
        • 1. 方法签名不匹配
        • 2. 提高代码可读性和语义清晰性
        • 3. 多播委托的需求
        • 4. 事件处理
        • 5. 异步操作
        • 6. 泛型约束
      • 总结

什么是委托(Delegate)

委托(Delegate)是一种类型安全的函数指针,它允许你将方法作为参数传递给其他方法,或者将方法赋值给变量。委托在 C# 中用于事件处理、回调函数、多播委托等场景。

  • 委托的声明

委托的声明类似于方法的声明,但不包含方法体。例如:

public delegate void MyDelegate(string message);

这个声明定义了一个名为 MyDelegate 的委托类型,它接受一个 string 类型的参数并且没有返回值。

  • 委托的实例化

你可以将一个方法赋值给委托实例,只要方法的签名与委托的签名匹配。例如:

public void PrintMessage(string message)
{
    Console.WriteLine(message);
}

MyDelegate myDelegate = new MyDelegate(PrintMessage);
myDelegate("Hello, World!");

单播委托(Unicast Delegate)

单播委托是指只能引用一个方法的委托。这是 C# 中最常见的委托类型。

示例代码:

using System;

// 1. 定义委托类型
public delegate void MyDelegate(string message);

public class Program
{
    // 定义一个方法,它的签名与委托类型匹配
    public static void Method1(string message)
    {
        Console.WriteLine("Method1: " + message);
    }

    public static void Main()
    {
        // 2. 创建委托实例并关联方法
        MyDelegate del1 = new MyDelegate(Method1);

        // 3. 调用委托
        del1("Hello, World!");
    }
}

输出结果:

Method1: Hello, World!

解释:

  • 在上面的示例中,我们首先定义了一个名为 MyDelegate 的委托类型,它接受一个 string 类型的参数,没有返回值。
  • 然后定义了一个方法 Method1,它的签名与 MyDelegate 匹配。
  • 创建了一个委托实例 del1,并将其关联到 Method1
  • 调用 del1 时,Method1 被执行。

多播委托(Multicast Delegate)

委托可以组合多个方法,形成一个多播委托。多播委托是指可以引用多个方法的委托。通过使用多播委托,可以将多个方法组合在一起,形成一个调用列表。

示例代码:

using System;

// 1. 定义委托类型
public delegate void MyDelegate(string message);

public class Program
{
    // 定义两个方法,它们的签名与委托类型匹配
    public static void Method1(string message)
    {
        Console.WriteLine("Method1: " + message);
    }

    public static void Method2(string message)
    {
        Console.WriteLine("Method2: " + message);
    }

    public static void Main()
    {
        // 2. 创建委托实例并关联方法
        MyDelegate del1 = new MyDelegate(Method1);
        MyDelegate del2 = new MyDelegate(Method2);

        // 3. 组合委托
        MyDelegate multicastDelegate = del1 + del2;

        // 4. 调用委托
        multicastDelegate("Hello, World!");

        // 5. 移除方法
        multicastDelegate -= del1;

        // 再次调用委托
        multicastDelegate("Hello again!");
    }
}

输出结果:

Method1: Hello, World!
Method2: Hello, World!
Method2: Hello again!

解释:

  • 在上面的示例中,我们首先定义了一个名为 MyDelegate 的委托类型,它接受一个 string 类型的参数,没有返回值。
  • 然后定义了两个方法 Method1Method2,它们的签名与 MyDelegate 匹配。
  • 创建了两个委托实例 del1del2,分别关联 Method1Method2
  • 使用 + 运算符将 del1del2 组合成一个多播委托 multicastDelegate
  • 调用 multicastDelegate 时,Method1Method2 会按顺序被调用。
  • 使用 - 运算符从 multicastDelegate 中移除 del1,再次调用时只有 Method2 被执行。

通过这种方式,多播委托为 C# 提供了一种强大的机制来处理 事件和回调,使得代码更加灵活和可扩展。

内置委托(Action & Func)

当然,我们可以使用 C# 内置的 ActionFunc 委托来实现 单播和多播委托 。下面我会分别给出示例代码。

单播委托(使用 Action 和 Func)

示例代码:

using System;

public class Program
{
    // 定义一个方法,它的签名与 Action<string> 匹配
    public static void PrintMessage(string message)
    {
        Console.WriteLine("Message: " + message);
    }

    // 定义一个方法,它的签名与 Func<int, int, int> 匹配
    public static int Add(int a, int b)
    {
        return a + b;
    }

    public static void Main()
    {
        // 单播委托示例:使用 Action<string>
        Action<string> actionDelegate = new Action<string>(PrintMessage);
        actionDelegate("Hello, World!");

        // 单播委托示例:使用 Func<int, int, int>
        Func<int, int, int> funcDelegate = new Func<int, int, int>(Add);
        int result = funcDelegate(3, 4);
        Console.WriteLine("Result from funcDelegate: " + result);
    }
}

输出结果:

Message: Hello, World!
Result from funcDelegate: 7

解释:

  • 使用 Action<string> 委托来实现单播委托,关联 PrintMessage 方法。
  • 使用 Func<int, int, int> 委托来实现单播委托,关联 Add 方法。
  • 调用这些委托时,分别执行关联的方法。
多播委托(使用 Action)

Action 委托在 C# 中通常用于表示无返回值的方法。它是一组预定义的委托类型之一,旨在简化代码编写并提高可读性。

示例代码:

using System;

public class Program
{
    // 定义两个方法,它们的签名与 Action<string> 匹配
    public static void PrintMessage1(string message)
    {
        Console.WriteLine("Message1: " + message);
    }

    public static void PrintMessage2(string message)
    {
        Console.WriteLine("Message2: " + message);
    }

    public static void Main()
    {
        // 创建 Action<string> 委托实例并关联方法
        Action<string> actionDelegate1 = new Action<string>(PrintMessage1);
        Action<string> actionDelegate2 = new Action<string>(PrintMessage2);

        // 组合委托
        Action<string> multicastDelegate = actionDelegate1 + actionDelegate2;

        // 调用委托
        multicastDelegate("Hello, World!");

        // 移除方法
        multicastDelegate -= actionDelegate1;

        // 再次调用委托
        multicastDelegate("Hello again!");
    }
}

输出结果:

Message1: Hello, World!
Message2: Hello, World!
Message2: Hello again!

解释:

  • 使用 Action<string> 委托来实现多播委托,关联 PrintMessage1PrintMessage2 方法。
  • 使用 + 运算符将 actionDelegate1actionDelegate2 组合成一个多播委托 multicastDelegate
  • 调用 multicastDelegate 时,PrintMessage1PrintMessage2 会按顺序被调用。
  • 使用 - 运算符从 multicastDelegate 中移除 actionDelegate1,再次调用时只有 PrintMessage2 被执行。
多播委托(使用 Func)

Func 委托通常用于返回值,因此多播委托的使用场景较少。不过,我们可以通过组合多个 Func 委托来实现类似的效果,但需要注意的是,Func 委托的返回值会覆盖之前的返回值。

示例代码:

using System;

public class Program
{
    // 定义两个方法,它们的签名与 Func<int, int> 匹配
    public static int Increment(int value)
    {
        return value + 1;
    }

    public static int Double(int value)
    {
        return value * 2;
    }

    public static void Main()
    {
        // 创建 Func<int, int> 委托实例并关联方法
        Func<int, int> funcDelegate1 = new Func<int, int>(Increment);
        Func<int, int> funcDelegate2 = new Func<int, int>(Double);

        // 组合委托
        Func<int, int> multicastFunc = funcDelegate1 + funcDelegate2;

        // 调用委托
        // 注意:Func 的多播委托调用会覆盖之前的返回值
        int result = multicastFunc(3);
        Console.WriteLine("Result from multicastFunc: " + result);
    }
}

输出结果:

Unhandled Exception: System.MulticastNotSupportedException: Multicast delegate invocation with non-void return type is not allowed.

解释:

  • 使用 Func<int, int> 委托来尝试实现多播委托,关联 IncrementDouble 方法。
  • 尝试使用 + 运算符将 funcDelegate1funcDelegate2 组合成一个多播委托 multicastFunc
  • 调用 multicastFunc 时,会抛出 MulticastNotSupportedException 异常,因为 Func 委托的多播调用不支持非 void 返回类型。

使用内置 Action 和 Func 的好处

Action 委托

Action 委托是 C# 提供的内置委托类型,用于表示没有返回值的方法。Action 委托有多个重载版本,可以接受 016 个参数。

Func 委托

Func 委托是 C# 提供的内置委托类型,用于表示有返回值的方法。Func 委托有多个重载版本,可以接受 016 个参数,并且最后一个参数是返回值类型。

推荐使用他们的好处:

  • 简洁性:使用 Action & Func 可以避免显式声明委托类型,使代码更简洁。
  • 类型安全: Action & Func 是类型安全的函数指针,允许你将方法作为参数传递或赋值给变量,编译器会检查参数类型和数量。
  • 内置支持: Action & FuncC# 标准库的一部分,无需额外定义。

他们的区别:

  • Action:用于表示 没有返回值的方法,有多个重载版本,可以接受 016 个参数。
  • Func:用于表示 有返回值的方法,有多个重载版本,可以接受 016 个参数,并且最后一个参数是返回值类型

什么情况下才需要自定义委托

自定义委托在某些特定情况下是非常有用的,尤其是在现有的内置委托(如 ActionFunc)无法满足需求时。以下是需要自定义委托的一些常见场景:

1. 方法签名不匹配

当你的方法签名与现有的 ActionFunc 委托不匹配时,你需要定义一个自定义委托。例如,如果你有一个方法接受特定类型的参数并返回特定类型的值,而这些类型不是通用的或数量超过 Func 和 Action 支持的最大参数数量(16个),那么你需要自定义委托。

示例:

public delegate bool CustomDelegate(int x, string y, double z);
2. 提高代码可读性和语义清晰性

有时为了提高代码的可读性和语义清晰性,即使方法签名可以使用 ActionFunc,你仍然可以选择定义一个自定义委托。通过给委托一个有意义的名字,可以使代码更具描述性。

public delegate void EventHandler(object sender, EventArgs e);

// 使用自定义委托使代码更易读
public event EventHandler OnDataReceived;
3. 多播委托的需求

当你需要将多个方法组合成一个多播委托,并且希望这些方法具有特定的签名时,自定义委托可以提供更好的控制和灵活性。

示例:

public delegate void NotificationHandler(string message);

NotificationHandler handler = null;

void RegisterNotifications()
{
    handler += HandleNotification1;
    handler += HandleNotification2;
}

void HandleNotification1(string message)
{
    Console.WriteLine("Handler 1: " + message);
}

void HandleNotification2(string message)
{
    Console.WriteLine("Handler 2: " + message);
}
4. 事件处理

C# 中,事件通常使用委托来定义。虽然你可以使用 EventHandlerEventHandler<TEventArgs>,但在某些情况下,你可能需要定义一个更具体的委托来处理特定类型的事件。

示例:

public class DataChangedEventArgs : EventArgs
{
    public int OldValue { get; }
    public int NewValue { get; }

    public DataChangedEventArgs(int oldValue, int newValue)
    {
        OldValue = oldValue;
        NewValue = newValue;
    }
}

public delegate void DataChangedEventHandler(object sender, DataChangedEventArgs e);

public class DataSource
{
    public event DataChangedEventHandler DataChanged;

    protected virtual void OnDataChanged(DataChangedEventArgs e)
    {
        DataChanged?.Invoke(this, e);
    }
}
5. 异步操作

对于复杂的异步操作,特别是那些涉及回调函数的情况,自定义委托可以帮助更好地组织和管理代码。

示例:

public delegate void AsyncOperationCompletedHandler(bool success, string result);

public class AsyncService
{
    public void PerformAsyncOperation(AsyncOperationCompletedHandler callback)
    {
        // 模拟异步操作
        Task.Run(() =>
        {
            // 操作完成后调用回调
            callback(true, "Operation completed successfully");
        });
    }
}
6. 泛型约束

有时你需要对委托中的泛型参数施加约束,这在 Func 和 Action 中是无法直接实现的。通过自定义委托,你可以添加泛型约束以确保类型安全。

示例:

public delegate TResult CustomGenericDelegate<in T, out TResult>(T arg) where T : class;

通过自定义委托,你可以更灵活地处理各种编程需求,同时保持代码的清晰性和可维护性。如果你发现现有的 ActionFunc 委托已经足够满足需求(通常大多数情况下内置委托类型已经足够需求),则无需额外定义自定义委托。

总结

  • ActionFuncC# 中非常方便的委托类型,可以简化委托的定义和使用。
  • Action 适用于没有返回值的方法。
  • Func 适用于有返回值的方法。
  • 多播委托通常使用 Action 来实现,而 Func 由于其返回值特性,多播调用不常用。

希望这些示例能帮助你更好地理解如何使用 ActionFunc 来实现单播和多播委托。

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

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

相关文章

【工具整理】WIN换MAC机器使用工具整理

最近公司电脑升级&#xff0c;研发同学统一更换了 Mac Book Pro 笔记版电脑&#xff0c;整理一下安装了那些软件以及出处&#xff0c;分享记录下&#xff5e; 知识库工具 1、语雀 网址&#xff1a;语雀&#xff0c;为每一个人提供优秀的文档和知识库工具 语雀 个人花园&…

【GUI-pyqt5】QWidget类

1. 描述 所有可视空间的基类是一个最简单的空白控件控件是用户界面的最小元素 接收各种事件&#xff08;鼠标、键盘&#xff09;绘制在桌面上&#xff0c;显示给用户看 每个控件都是矩形的&#xff0c;它们按z轴顺序排序控件由其父控件和前面的控件剪切没有父控件的控件&#…

SpringBoot Maven 项目 pom 中的 plugin 插件用法整理

把 SpringBoot Maven 项目打包成 jar 文件时&#xff0c;我们通常用到 spring-boot-maven-plugin 插件。 前面也介绍过&#xff0c;在 spring-boot-starter-parent POM 和 spring-boot-starter POM 中都有插件的管理&#xff0c;现在我们就撸一把构建元素中插件的用法。 一、…

springboot实战纪实-课程介绍

教程介绍 Spring Boot是由Pivotal团队提供的一套开源框架&#xff0c;可以简化spring应用的创建及部署。它提供了丰富的Spring模块化支持&#xff0c;可以帮助开发者更轻松快捷地构建出企业级应用。 Spring Boot通过自动配置功能&#xff0c;降低了复杂性&#xff0c;同时支持…

源代码编译安装X11及相关库、vim,配置vim(2)

一、编译安装vim 编译时的cofigure选项如下.只有上一步的X11的包安装全了&#xff08;具体哪些是必须的&#xff0c;哪些是多余的没验证&#xff09;&#xff0c;configure才能认为X的库文件和头文件是可以用的 ./configure --prefixpwd/mybuild \--x-includes/path/to/X11/m…

直接插入排序、折半插入排序、2路插入排序、希尔排序

本篇是排序专栏博客的第一篇&#xff0c;主要探讨以 “插入” 为核心思想的排序算法该如何实现 文章目录 一、前言二、直接插入排序1. 算法思想与操作分析2. 代码实现version 1version 2 3. 复杂度分析 三、折半插入排序1. 算法思想与操作分析2. 代码实现3. 复杂度分析 四、2路…

Ansible之批量管理服务器

文章目录 背景第一步、安装第二步、配置免密登录2.1 生成密钥2.2 分发公钥2.3 测试无密连接 背景 Ansible是Python强大的服务器批量管理 第一步、安装 首先要拉取epel数据源&#xff0c;执行以下命令 yum -y install epel-release安装完毕如下所示。 使用 yum 命令安装 an…

让 Agent 具备语音交互能力:技术突破与应用前景(16/30)

让 Agent 具备语音交互能力&#xff1a;技术突破与应用前景 一、引言 在当今数字化时代&#xff0c;人机交互方式正经历着深刻的变革。从早期的命令行界面到图形用户界面&#xff0c;再到如今日益普及的语音交互&#xff0c;人们对于与机器沟通的便捷性和自然性有了更高的追求…

学生作业完成情况管理程序

网上看到的一个课程设计,正好练练手。 首先设计数据库 数据库有三张表&#xff0c;分别是班级表&#xff0c;学生表&#xff0c;作业成绩表。 学生表中外键关联班级表&#xff0c;作业成绩表中外键关联学生表。具体如下图所示 班级表 学生表学生表外键关联 …

基于vue的商城小程序的毕业设计与实现(源码及报告)

环境搭建 ☞☞☞ ​​​Vue入手篇(一)&#xff0c;防踩雷(全网最详细教程)_vue force-CSDN博客 目录 一、功能介绍 二、登录注册功能 三、首页 四、项目截图 五、源码获取 一、功能介绍 用户信息展示&#xff1a;页面顶部设有用户头像和昵称展示区&#xff0c;方便用户识别…

DeepSeek V3“报错家门”:我是ChatGPT

搜 &#xff1a;海讯无双Ai 要说这两天大模型圈的顶流话题&#xff0c;那绝对是非DeepSeek V3莫属了。 不过在网友们纷纷测试之际&#xff0c;有个bug也成了热议的焦点—— 只是少了一个问号&#xff0c;DeepSeek V3竟然称自己是ChatGPT。 甚至让它讲个笑话&#xff0c;生成…

利用webworker解决性能瓶颈案例

目录 js单线程的问题webworker的基本使用webworker的常见应用可视化优化导出Excel js单线程的问题 众所周知&#xff0c;js不擅长计算&#xff0c;计算是同步的&#xff0c;大规模的计算会让js主线程阻塞&#xff0c;导致界面完成卡死。比如有一个600多亿次的计算&#xff0c;…

深入理解卷积神经网络(CNN):图像识别的强大工具

1、引言 卷积神经网络&#xff08;CNN&#xff09;是一种深度学习模型&#xff0c;特别适合分析视觉数据。它们在处理图像和视频任务时表现尤为出色。由于CNN在物体识别方面的高效性&#xff0c;这种网络架构广泛应用于计算机视觉领域&#xff0c;例如图像分类、物体检测、面部…

R语言安装教程与常见问题

生物信息基础入门笔记 R语言安装教程与常见问题 今天和大家聊一个非常基础但是很重要的技术问题——如何在不同操作系统上安装R语言&#xff1f;作为生物信息学数据分析的神兵利器&#xff0c;R语言的安装可谓是入门第一步&#xff0c;学术打工人的必备技能。今天分享在Windows…

VOC数据集格式转YOLO格式

将VOC格式的数据集转换为YOLO格式通常涉及以下几个步骤。YOLO格式的标注文件是每个图像对应一个.txt文件&#xff0c;文件中每一行表示一个目标&#xff0c;格式为&#xff1a; <class_id> <x_center> <y_center> <width> <height>其中&#xf…

win10搭建zephyr开发环境

搭建环境基于 zephyr官方文档 基于官方文档一步一步走很快就可以搞定 一、安装chocolatey 打开官网 https://community.chocolatey.org/courses/installation/installing?methodinstall-from-powershell-v3 1、用管理员身份打开PowerShell &#xff08;1&#xff09;执行 …

物体切割效果

1、物体切割效果是什么 在游戏开发中&#xff0c;物体切割效果就是物体看似被切割、分割或隐藏一部分的视觉效果。 这种效果常用与游戏和动画中&#xff0c;比如角色攻击时的切割效果&#xff0c;场景中的墙壁切割效果等等。 2、物体切割效果的基本原理 在片元着色器中判断片…

k8s集群监控系统部署方案

1.方案介绍 本文介绍一种k8s集群监控系统,该系统可以监控k8s集群中的pod和node的性能指标,以及K8s资源对象的使用情况。 监控流程: 集群资源数据采集(cadvisor、node-exporter、kube-state-metrics)-- 数据收集、存储、处理等(prometheus)-- 数据可视化查询和展示(gra…

RP2K:一个面向细粒度图像的大规模零售商品数据集

这是一种用于细粒度图像分类的新的大规模零售产品数据集。与以往专注于相对较少产品的数据集不同&#xff0c;我们收集了2000多种不同零售产品的35万张图像&#xff0c;这些图像直接在真实的零售商店的货架上拍摄。我们的数据集旨在推进零售对象识别的研究&#xff0c;该研究具…

Linux(Centos 7.6)命令详解:ls

1.命令作用 列出目录内容(list directory contents) 2.命令语法 Usage: ls [OPTION]... [FILE]... 3.参数详解 OPTION: -l&#xff0c;long list 使用长列表格式-a&#xff0c;all 不忽略.开头的条目&#xff08;打印所有条目&#xff0c;包括.开头的隐藏条目&#xff09…