0

0

通过IL分析C#中的委托、事件、Func、Action、Predicate之间的区别与联系

黄舟

黄舟

发布时间:2017-02-17 11:02:14

|

1686人浏览过

|

来源于php中文网

原创

一直以来都是对于事件与委托比较混淆,而且不太会用。找了个时间,总结了一下,感觉清晰了很多。

先说一下个人理解的结论吧:

      delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。

     delegate声明的变量与delegate声明的事件,并没有本质的区别,事件是在delegate声明变量的基础上包装而成的,类似于变量与属性的关系(在IL代码中可以看到每一个delegate声明的事件都对应是私有的delegate声明的变量),提升了安全性。

      Action 与Func:这两个其实说白了就是系统定义好的Delegate,他有很多重载的方法,便于各种应用情况下的调用。他在系统的System命名空间下,因此全局可见。

首先了解一下, ILDasm中图标含义:  

   

该图来自:http://www.php.cn/

委托创建步骤:

1、用delegate关键字创建一个委托,包括声明返回值和参数类型
2、使用的地方接收这个委托
3、创建这个委托的实例并指定一个返回值和参数类型匹配的方法传递过去


一、事件与委托



新建一个事件委托测试项目:EventDelegateTest

具体代码如下:

namespace EventDelegateTest
{
    public class TestClass
    {
        public delegate int delegateAction();
        public event delegateAction OnActionEvent;
        public delegateAction daNew;
    }
}

编译代码后,使用 visual studio 2010自带的ildasm.exe:


打开该dll,可以看到如下信息:



从上图可以看出如下几点信息:

1、委托 public delegate int delegateAction();

      在IL中是以类(delegateAction)的形式存在的

      .NET将委托定义为一个密封类,派生自基类System.MulticastDelegate,并继承了基类的三个方法


2、public event delegateAction OnActionEvent;

      在IL中不仅仅对应event OnActionEvent而且还对应一个field OnActionEvent;

而field OnActionEvent与 public delegateAction daNew生成的field daNew是一样的




都是以字段(field )的形式存在的。

双击event OnActionEvent可以看到如下信息:



在IL中事件被封装成了包含一个add_前缀和一个remove_前缀的的代码段。

其中,add_前缀的方法其实是通过调用Delegate.Combine()方法来实现的,组成了一个多播委托;remove_就是调用Delegate.Remove()方法,用于移除多播委托中的某个委托。

也就是说:事件其实就是一个特殊的多播委托

那么对于事件进行这一次封装有什么好处呢?

1、因为delegate可以支持的操作非常多,比如我们可以写onXXXChanged += aaaFunc,把某个函数指针挂载到这个委托上面,但是我们也可以简单粗暴地直接写onXXXChanged = aaaFunc,让这个委托只包含这一个函数指针。不过这样一来会产生一个安全问题:如果我们用onXXXChanged = aaaFunc这样的写法,那么会把这个委托已拥有的其他函数指针给覆盖掉,这大概不是定义onXXXChanged的程序员想要看到的结果。

小注:

    虽然事件不能直接=某个函数,也不可以直接=null


2、还有一个问题就是onXXXChanged这个委托应该什么时候触发(即调用它所包含的函数指针)。从面向对象的角度来说,XXX改变了这个事实(即onXXXChaned的字面含义)应该由包含它的那个对象来决定。但实际上我们可以从这个对象的外部环境调用onXXXChanged,这既产生了安全问题也不符合面向对象的初衷。 

说到这里对于事件与委托的管理算是说明白了,那么平时常用的Action与Func,与委托又有什么关系呢?


二、Action 与Func


Action 委托:封装一个方法,该方法具有参数(0到16个参数)并且不返回值。

具体形式如下:http://www.php.cn/(v=vs.110).aspx


Func 委托:封装一个具有参数(0到16个参数)并返回 TResult 参数指定的类型值的方法。

HaiSnap
HaiSnap

一站式AI应用开发和部署工具

下载

具体形式如下:http://www.php.cn/(v=vs.110).aspx


那么这Action与Func是怎么实现的呢?

1、Action(以Action 委托:封装一个方法,该方法具有两个参数并且不返回值为例)

从微软公布的源码中,可以看到,如下实现:


public Action  ac;

上面这个声明就是:该方法具有两个参数并且不返回值的委托。

其余使用方式与委托变量一样。

2、Func(以Func 委托:封装一个具有两个参数并返回 TResult 参数指定的类型值的方法为例)
从微软公布的源码中,可以看到,如下实现:



此处,可以看出Func与Action是类似的,唯一的区别就是,Func必须指定返回值的类型,使用方式与委托咱们自己使用委托变量是一样的,直接使用相应参数的Func或者Action声明变量,=或者+=挂载函数(方法即可)

这两个其实说白了就是系统定义好的Delegate,他有很多重载的方法,便于各种应用情况下的调用。他在系统的System命名空间下,因此全局可见。


三、Predicate


是返回bool型的泛型委托,Predicate有且只有一个参数,返回值固定为bool。表示定义一组条件并确定指定对象是否符合这些条件的方法。此方法常在集合(Array 和 List)的查找中被用到,如:数组,正则拼配的结果集中被用到。

官方文档:点击打开链接


具体用法demo如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace IconTest
{
    public partial class Form2 : Form
    {
        Predicate myPredicate;
        int[] myNum = new int[8] { 12, 33, 89, 21, 15, 29, 40, 52 };
        public int[] myResult;
        public Form2()
        {
            InitializeComponent();
            myPredicate = delegate(int curNum)             
            {
                if (curNum % 2 == 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            };
        }
        private void Form2_Load(object sender, EventArgs e)
        {
            myResult = Array.FindAll(myNum, myPredicate);
        }          
    }
}

上例中说明了Predicate的使用,FindAll方法中,参数2即是一个Predicate,在具体的执行中,每一个数组的元素都会执行指定的方法,如果满足要求返回true,并会被存放在结果集中,不符合的则被剔除,最终返回的集合,即是结果判断后想要的集合。
Array.FindAll 泛型方法:点击打开链接

以上代码执行结果为:


那么Predicate与委托又有什么关系呢?


从微软源码中可以看出Predicate是返回bool型的泛型委托,从本质上来说与Func、Action、事件、委托变量并无本质区别。


 以上就是通过IL分析C#中的委托、事件、Func、Action、Predicate之间的区别与联系的内容,更多相关内容请关注PHP中文网(www.php.cn)!



热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

14

2026.01.30

c++ 字符串格式化
c++ 字符串格式化

本专题整合了c++字符串格式化用法、输出技巧、实践等等内容,阅读专题下面的文章了解更多详细内容。

9

2026.01.30

java 字符串格式化
java 字符串格式化

本专题整合了java如何进行字符串格式化相关教程、使用解析、方法详解等等内容。阅读专题下面的文章了解更多详细教程。

12

2026.01.30

python 字符串格式化
python 字符串格式化

本专题整合了python字符串格式化教程、实践、方法、进阶等等相关内容,阅读专题下面的文章了解更多详细操作。

4

2026.01.30

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

20

2026.01.29

java配置环境变量教程合集
java配置环境变量教程合集

本专题整合了java配置环境变量设置、步骤、安装jdk、避免冲突等等相关内容,阅读专题下面的文章了解更多详细操作。

18

2026.01.29

java成品学习网站推荐大全
java成品学习网站推荐大全

本专题整合了java成品网站、在线成品网站源码、源码入口等等相关内容,阅读专题下面的文章了解更多详细推荐内容。

19

2026.01.29

Java字符串处理使用教程合集
Java字符串处理使用教程合集

本专题整合了Java字符串截取、处理、使用、实战等等教程内容,阅读专题下面的文章了解详细操作教程。

3

2026.01.29

Java空对象相关教程合集
Java空对象相关教程合集

本专题整合了Java空对象相关教程,阅读专题下面的文章了解更多详细内容。

6

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号