Maison  >  Article  >  développement back-end  >  Analyser les différences et les connexions entre les délégués, les événements, Func, Action et Predicate en C# via IL

Analyser les différences et les connexions entre les délégués, les événements, Func, Action et Predicate en C# via IL

黄舟
黄舟original
2017-02-17 11:02:141515parcourir

J'ai toujours été confus au sujet des événements et de la délégation, et je ne suis pas très doué pour les utiliser. Après avoir pris le temps de résumer, je me sens beaucoup plus clair.

Permettez-moi d'abord de parler de ma compréhension personnelle :

Delegate est un type en C# , qui est en fait une classe qui peut contenir une référence à une méthode.

Il n'y a pas de différence essentielle entre les variables déclarées par le délégué et les événements déclarés par le délégué. Les événements sont empaquetés sur la base des variables déclarées par le délégué, similaire à la relation. entre variables et attributs (vous pouvez voir dans le code IL que chaque événement déclaré par un délégué correspond à une variable déclarée par un délégué privé) améliore la sécurité.

Action et Func : ces deux-là sont en fait le délégué défini par le système. Il dispose de nombreuses méthodes surchargées pour faciliter diverses situations d'appel. Il se trouve sous l'espace de noms System du système et est donc visible globalement.

Tout d'abord, comprenons la signification des icônes dans ILDasm :

Cette image provient de : http://www.php.cn/

Étapes de création d'une délégation :

1. Utiliser le délégué Le mot-clé crée un délégué, notamment en déclarant la valeur de retour et le type de paramètre
2. Recevez le délégué là où il est utilisé
3. Créez une instance de ce délégué et spécifiez une méthode dont la valeur de retour correspond à la tapez le paramètre et transmettez-le là


Événements et délégations



Créer un nouveau projet de test de délégation d'événement : EventDelegateTest

Le code spécifique est le suivant :

<span style="font-size:14px;"><span style="font-size:14px;">namespace EventDelegateTest
{
    public class TestClass
    {
        public delegate int delegateAction();
        public event delegateAction OnActionEvent;
        public delegateAction daNew;
    }
}</span></span>

Après avoir compilé le code, utilisez le ILDASM.EXE fourni avec Visual Studio 2010 :



Ouvrez la dll et vous pourrez voir les informations suivantes :



De ce qui précède Sur l'image, nous pouvons voir les informations suivantes :

1. Délégation public délégué int déléguéAction();

existe sous la forme d'une classe ( déléguéAction) dans IL

.NET définit un délégué comme une classe scellée, dérivée de la classe de base System.MulticastDelegate, et hérite des trois méthodes de la classe de base


2. délégué d'événement publicAction OnActionEvent;

En IL, cela ne correspond pas seulement à l'événement OnActionEvent mais correspond également à un champ OnActionEvent;

Le champ daNew généré par le champ OnActionEvent et public déléguéAction daNew est le même





都是以字段(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


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

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


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

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

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


public Action<bool,bool>  ac;

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


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

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



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

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


三、Predicate


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

官方文档:点击打开链接


具体用法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<int> 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 泛型方法:点击打开链接


以上代码执行结果为:


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


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




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



Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn