C#的委托和事件详细分析

看到网上的一些对委托与事件的解释,感觉会有点误导,所以这里准备详细的解剖一下委托与事件,先提出几点的问题,1.委托是相当于C++中的函数指针吗?2.事件和委托的区别是什么,Action和Func是什么?3.委托有哪些高级点的用法?

1.委托是一个类,不是函数指针

首先直接上图就是经过反编译DLL的委托语法:

本来的语法是delegate void simpleHandle(),说明使用delegate修饰符创造出了一个成员变量只和无参数无返回函数有关并继承MulticastDelegate的一个类,这个类之后只能使用无参数无返回类型的函数,比如调用委托的构造函数,-=,+=2个重载运算符等等只能使用无参数无返回类型的函数作为参数,所以结论就是委托就是一个继承于MulticastDelegate的一个类,它里面的成员变量应该有类似函数指针列表的变量,大家有兴趣可以是ILSpy这个软件去看一下MulticastDelegate具体实现(还有一个提外话,我们可以发现所有类的基类都是Object,经过反编译以后也证实这个观点了)

2.事件其实是委托实例的权限修饰符

 通过static event simpleHandle sEventHandle从语法上分析,可以发现simpleHandle这个类去创建实例的时候,添加了event修饰符对sEventHandle实例会有一些权限的限制,网上说事件就是委托的特殊实例,这个对于刚接触C#的人会误导很大,事件对委托实例来说只是修饰符用来修饰委托这个类创建出来的实例,如果非要说sEventHandle这个实例的叫做事件,那我无可反驳,event只能修饰继承MulticastDelegate的类,修饰其他类会出现错误,外部调用sEventHandle实例的时候只能+=,-=,类其他的方法都不能使用,如此限制不知道是更加安全还是其他什么,这里不做评论,如图下:

之后就是Action和Func到底是什么呢?先看看Action是什么,如图下:

F12进入Action实现,可以发现无参无返回的Action的实现不就是我们定义委托的语法吗?所以Action是.Net给我们定义好的委托,Action必定是无返回的泛型委托,然后再查看一下Func的实现,如图下:

Func可以看到也是.Net给我们定义好的委托,Func是必定有返回的泛型委托,所以我们需要定义无返回的委托就直接使用Action,定义有返回的委托直接使用Func就可以了,它们的参数最大支持16个参数,也就是0~16个参数,.Net已经给我们定义这么多泛型委托了,如图下:

3.委托的一些高级用法

上面已经把委托介绍清楚了,我们在编程设计需要的时候委托都可以使用,我在下面介绍一些需要使用委托的列子,接触过C++应该都知道模板库提供给我们qsort这个排序API,调用它的时候需要把排序函数指针作为参数传递给qsort,qsort会使用这个函数进行快排,所以这个在C#中也可以这样类似的使用,我们知道linq查询操作就是使用这种设计结构的,这个好处就是把具体的动作交给外部去指定,代码不用写死了,或者分很多种情况的时候,不需要把动作枚举传递进去,代码中也不需要判断使用什么动作去实现这个功能,比如qsort排序是从大到小还是从小到大或者其他的排序方式都可以由我们传递进去的函数指针决定,还有就是之前在公司实习的时候因为在开发项目之前没有考虑到有重启游戏这个功能,和主程商量的时候改起来太麻烦了,只能真的去重启游戏了(本来应该是点击浮标中切换账号的按钮,在任何界面都可以返回到游戏的登录界面),现在思考一下如果开始就知道有这个需求,可以在每一个界面启动的时候把需要回收的函数注册到全局静态委托中,具体代码如下:

public class ShowRewardBehavior : ViewBehaviour {
        public void Start()
        {
            MessageDefinition.restartGameHandle -= RestartGameEvent;
            MessageDefinition.restartGameHandle += RestartGameEvent;
        }

        public void OnDestroy()
        {
            MessageDefinition.restartGameHandle -= RestartGameEvent;
        }

        public void RestartGameEvent()
        {
            //TODO  注册清理此界面的函数
        }
}

可以看到在显示奖励界面开启时把消息注册到全局静态函数中了,这里有一个保护就是先把此界面重启函数清理到,再进行注册,防止这个函数因为一些问题注册了2次(如果这个函数就不在这个多播委托里,-=也不会报异常错误),还有一点需要注意的-=的时候如果是匿名函数是不行的,也就是当初MessageDefinition.restartGameHandle += ()=> { }这样注册进去的,-=()=> { }是无法取消注册的,接下来查看一下定义全局静态函数的类,如图下:

using System;

public static class MessageDefinition
{
    public static Action restartGameHandle;
}

最后就是当点击这个按钮以后,我们应该如何调用这个多播委托比较好,MessageDefinition.restartGameHandle?.Invoke(),这样可以确保如果这个委托实例是null的话,也不会发生异常。

4.总结一下委托

 委托是什么?委托就是一个类继承于MulticastDelegate,event就是修饰委托实例的修饰符,修饰这个实例以后外部类只能调用-=和+=这2个重载运算符。

发布了26 篇原创文章 · 获赞 3 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/m0_37920739/article/details/92377797
今日推荐