c# 事件和委托,再也不忘了

0x00

c# 中的委托和事件一直是记了忘忘了记,反反复复,头大的很……

这次做一个系统的总结吧

0x01 Delegate

委托( delegate ),是一种存储函数引用的类型。声明一个委托与声明函数类似,区别在于不包含函数体,需要使用关键字 delegate。一个委托包含其可以引用的函数签名(返回类型和参数列表)。

可以完成类似传递“函数指针”作为参数的操作,进行回调。在运行之前只有这个变量即可,而不需要知道调用的是哪一个函数的引用。

  1. 定义一个委托类型
  2. 声明该委托类型的一个变量
  3. 将变量初始化为具有相同函数签名(返回值和参数列表)的函数引用
  4. 使用此委托变量调用它引用的任何函数

对于委托的初始化赋值,可以使用两种语法:

假设我们定义了一个委托变量 process,对应了上面的第一步和第二步。

delegate double ProcessDelegate(double arg1, double arg2);
ProcessDelegate process

我们的使用目的实在特定的打印位置调用一个类似这样的函数,如加减乘除四个方法,这样在该位置只调用 process 引用的方法就可以了,但在此之前,要根据需要对 process 进行初始化。

process = new ProcessDelegate(Multiply) // double Multiply(double arg1, double arg2)
process = Divide; // double Divide(double arg1, double arg2)

这个语法可能有点怪,需要特别记忆:使用 new 关键字创建了一个新的委托,指定了委托的类型,并提供了一个待引用的函数作为参数,这里只需要函数名就可以了。

而后面这种初始化方式,是将工作交给了编译器,编译器会判断委托类型与函数签名是否符合,然后自动地初始化一个委托。不像第一种可以一眼看出是一个委托的初始化。

在实际使用中,也不必经常重复地定义委托类型。c# 中提供了两种内置的委托,Action<arg1, arg2, …> 和 Func<arg1, arg2, …, return> 区别仅仅是是否要返回返回值(最后一个参数作为返回值)。

而 delegate 关键字还有另外一个作用,用作匿名函数,在这种情况下,使用 lambda 表达式也许更方便,但要知道匿名函数的本来面目是很容易混淆的 delegate 啊(其实了解了委托是函数引用也没那么容易混淆了)。

多播( Multicasting of a Delegate )
也称组播,委托对象可使用 “+” 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。"-" 运算符可用于从合并的委托中移除组件委托。这样就可以创建一个委托被调用时要调用的方法的调用列表。但是多播委托的调用顺序无法保证。

0x02 Event

事件类似于异常,都由对象引发(或抛出),然后通过提供的代码进行处理。事件需要在处理之前进行订阅( subscribe ),表示提供了在事件发生时需要执行的代码,即事件处理程序。C# 中的事件“event”关键字是语言层面的“观察者模式”的实现,将事件的发起者(被观察者)与处理者(观察者)的代码解耦。

事件处理程序需要被匹配事件所要求的返回值和参数,这个限制由委托来指定。

声明一个事件可以用这样的语句,要记得,Action 是没有返回值的内置委托类型:

public event Action<string, string, object[]> OnEvent;

而订阅的方式,类似这样:

MyClass.OnEvent += new MyDelegate(MyHandleFuncName);

***(?)***我的理解:事件只是在某种情况下,通过委托调用了一开始不知道是啥的函数。事件( event )关键字的作用是增加了订阅(+=)这个操作(并不是,这是委托的多播),让函数引用得以连接到事件对象上。所以事件的触发是以一种( 委托 )函数调用的方式进行的。

所以事件的定义离不来特定的委托类型,一个事件可以通过委托的 Invoke() 方法触发,进而使所有监听了这个事件的处理函数执行。

  1. 首先要定义一个委托类型
  2. 通过这个委托类型定义一个事件
  3. 在类内或外部,使用相同的委托绑定处理函数
  4. 将处理函数的委托订阅到事件
  5. 在触发事件时,即可通过委托执行相应处理函数

上面的例子中,使用内部委托,然后进行了第二步,随后将第三步第四步合并到了一行代码中,最后触发事件,使用:

OnEvent(str1, str2, payload);
OnEvent.?Invoke(str1, str2, payload);

使用( .? )避免为 null 时调用报错宕机。

多播可以来处理一个事件发生需要有多个处理程序响应的情况。

而还有一种情况是,要区分相应多个事件的同一个处理程序(称为多用途的事件处理程序)。为由不同对象引发的几个相同事件使用同一个事件处理程序,并且需要指定是由哪个对象生成了事件。

通过内置的委托 EventHandler 和 EventHandler 分别用来处理事件不提供数据和提供数据两种不同的处理方法。其中的类型 T 需要继承自 EventArgs,用来提供事件数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值