聊聊 C# 中的委托

聊聊 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.rhkb.cn/news/500696.html

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

相关文章

【Java回顾】Day2 正则表达式----异常处理

参考资料&#xff1a;菜鸟教程 https://www.runoob.com/java/java-exceptions.html 正则表达式 有一部分没看完 介绍 字符串的模式搜索、编辑或处理文本java.util.regex包&#xff0c;包含了pattern和mathcer类&#xff0c;用于处理正则表达式的匹配操作。 捕获组 把多个字符…

招银网路Java后端一面,难度有点大!

这是一位武汉理工大学同学的招银网络一面面经,同样附带超详细的参考答案。大家可以用来查漏补缺,针对性地补短板。 招银网络一面还是比较简单的,基本都是一些比较重要且高频的常规八股,项目问的不多。到了二面的时候, 会开始主要考察你的项目。 1、自我介绍 自我介绍一般…

xadmin后台首页增加一个导入数据按钮

xadmin后台首页增加一个导入数据按钮 效果 流程 1、在添加小组件中添加一个html页面 2、写入html代码 3、在urls.py添加导入数据路由 4、在views.py中添加响应函数html代码 <!DOCTYPE html> <html lang

arcgis模版空库怎么用(一)

这里以某个项目的数据为例&#xff1a; 可以看到&#xff0c;属性表中全部只有列标题&#xff0c;无数据内容 可能有些人会认为空库是用来往里面加入信息的&#xff0c;其实不是&#xff0c;正确的用法如下&#xff1a; 一、下图是我演示用的数据&#xff0c;我们可以看到其中…

GJB系统设计说明模板

GJB系统设计说明模板及详解 1 范围 1.1 标识 1.2 系统概述 1.3 文档概述 2 引用文档 GJB XXX XXX XXX&#xff1b; XXX XXX。 前2章通用不再赘述 3 系统级设计决策 系统设计决策的目的:对系统规格说明中的关键需求(包括功能、质量属性和设计约束)进行分析,得到系统级概念性架构…

某小程序sign签名参数逆向分析

文章目录 1. 写在前面2. 接口分析3. 分析还原 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python…

2000-2020年各省财政一般预算支出面板数据

2000-2020年各省财政一般预算支出面板数据 1、时间&#xff1a;2000-2020年 2、来源&#xff1a;国家统计局 3、指标&#xff1a;年份、省份、地方财政一般预算支出 4、范围&#xff1a;31省 指标解释&#xff1a;地方财政一般预算支出‌是指地方ZF根据预算安排&#xff0…

[羊城杯 2024]1z_misc

得到FL4G.zip和天机不可泄露.txt文件&#xff0c;其中压缩包需要解压密码&#xff1a; 二十八星宿&#xff1a; 东方苍龙七宿&#xff1a;角、亢、氐、房、心、尾、箕 南方朱雀七宿&#xff1a;鬼、井、柳、星、张、翼、轸 西方白虎七宿&#xff1a;奎、娄、胃、昴、毕、觜、…

右值引用全面剖析

为什么要有右值引用&#xff0c;右值引用出现前程序员们的困境&#xff1a; 在右值引用出现以前&#xff0c;想要把一块内存空间里的内容放到另一块内存空间&#xff0c;只能再开辟一块内存&#xff0c;然后将原来内存里的内容复制到新开辟的内存里&#xff0c;然后再把原来的…

mac下载Homebrew安装nvm

通过Homebrew安装 - 国内下载地址 /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"安装nvm brew install nvm 配置nvm环境变量 export NVM_DIR“$HOME/.nvm” [ -s “/usr/local/opt/nvm/nvm.sh” ] && . “/usr/…

解决chatgpt(mac app登陆)出现报错:获取您的 SSO 信息时出错

由于我们是app登陆的&#xff0c;不能直接修改网站的链接&#xff0c;将 URL 的域名部分从 auth.openai.com 变更为 auth0.openai.com&#xff0c;然后加载新的地址&#xff0c;这时候应该就可以正常登录或注册了。 所以我们使用邮箱先载入auth0的地址&#xff0c;再更改自己的…

C#编写的盘符图标修改器 - 开源研究系列文章

这天在网上遇到一个Windows的盘符图标修改软件&#xff0c;但是它那个是.net framework 2.0的&#xff0c;所以就将其改成4.8.1的了&#xff0c;用于Windows 11等默认不安装2.0库的操作系统里使用。 1、 项目目录&#xff1b; 2、 源码介绍&#xff1b; 它直接进行注册表的修改…

【第二部分--Python之基础】03 容器类型的数据

Python内置的数据类型如序列&#xff08;列表、元组等&#xff09;、集合和字典等可以容纳多项数据&#xff0c;我们称它们为容器类型的数据。 序列 序列&#xff08;sequence&#xff09;是一种可迭代的、元素有序的容器类型的数据。 序列包括列表&#xff08;list&#xff…

HTML5实现好看的二十四节气网页源码

HTML5实现好看的新年春节元旦网站源码 前言一、设计来源1.1 主界面1.2 关于我们界面1.3 春季节气界面1.4 夏季节气界面1.5 秋季节气界面1.6 冬季节气界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载结束语 HTML5实现好看的二十四节气网页源码&#xff0c;春季节气&#xf…

走进深圳华为总部参观研学

在这个科技日新月异的时代&#xff0c;每一次与行业标杆企业领先者对话&#xff0c;都是开眼界的好时机。华研标杆游学高老师组织了一场企业家参访团体考察&#xff0c;带大家去到深圳华为总部研学&#xff0c;亲身感受科技巨头的风采&#xff0c;一起探讨未来的发展。 第一站-…

【unity错误】Unity 6 LTS 打开就报错Assertion failed on expressionxxx?

unity6发布已经有一段时间了&#xff0c;如果目前你已经使用了unity6进行项目开发&#xff0c;可能打开会发现如下报错 Assertion failed on expression: ‘!(o->TestHideFlag(Object::kDontSaveInEditor) && (options & kAllowDontSaveObjectsToBePersistent) …

集线器,交换机,路由器,mac地址和ip地址知识记录总结

一篇很不错的视频简介 基本功能 从使用方面来说&#xff0c;都是为了网络传输的标识&#xff0c;和机器确定访问对象 集线器、交换机和路由器 常听到路由器和集线器&#xff0c;下面是区别&#xff1a; 集线器 集线器&#xff1a;一个简单的物理扩展接口数量的物理硬件。…

【ArcGISPro/GeoScenePro】检查并处理高程数据

数据 https://arcgis.com/sharing/rest/content/items/535efce0e3a04c8790ed7cc7ea96d02d/data 数字高程模型 (DEM) 是一种栅格,可显示地面或地形的高程。 数字表面模型 (DSM) 是另一种高程栅格,可显示表面的高度,例如建筑物或树冠的顶部。 您需要准备 DEM 和 DSM 以供分析…

《计算机组成及汇编语言原理》阅读笔记:p177-p177

《计算机组成及汇编语言原理》学习第 13 天&#xff0c;p177-p177 总结&#xff0c;总计 1 页。 一、技术总结 1.real mode A programming model where the program has access to the entire capability of the machine, bypassing security and memory management. Useful…

珞珈一号夜光遥感数据地理配准,栅格数据地理配准

目录 一、夜光数据下载&#xff1a; 二、夜光遥感数据地理配准 三、计算夜光数据值 四、辐射定标 五、以表格显示分区统计 五、结果验证 夜光数据位置和路网位置不匹配&#xff0c;虽然都是WGS84坐标系&#xff0c;不匹配&#xff01;&#xff01;&#xff01;不要看到就直接…