预备知识

在学习委托和事件之前,我们需要知道的是,很多程序都有一个共同的需求,即当一个特定的程序事件发生时,程序的其他部分可以得到该事件已经发生的通知。

发布者/订阅者模式可以满足这种需求。简单来说,在这种模式中,发布者定义了一系列程序的其他部分可能感兴趣的事件。其他类可以“注册”,以便再这些事件发生时发布者可以通知它们。这些订阅者类通过向发布者提供一个方法来“注册”以获取通知。当事件发生时,发布者“触发事件”,然后执行订阅者提交的所有事件。

注:由订阅者提供的方法称为回调方法,或者说回调函数,关于回调函数可以参考https://www.zhihu.com/question/1980113 。

大概了解了下基本机制后,我们再来看看委托和事件:

什么是委托?

委托和类一样,是一种用户自定义的类型。但类表示的是数据和方法的集合,而委托则持有一个或多个方法,以及一系列预定义操作(你可以暂时把它理解成一个类型安全的C++函数指针)。可以通过以下操作来使用委托:

1)声明一个委托类型(类似方法声明,但委托没有实现块)

public delegate void MyDel(int x);
//格式为[修饰符] [委托关键字delegate] [返回类型][委托名][签名]

2)使用该委托类型声明一个委托变量。

MyDel delVar;

3)创建委托类型的对象,把它赋值给委托对象。新的委托对象包括指向某个方法的引用,这个方法和第一步的签名和返回类型一致。

创建委托对象可以这样:

delVar = new MyDel(SomeClass.Method);
//创建委托并保存第一个个方法的引用

或者使用快捷语法,比如这样:

delVar = SomeClass.Method;

4 )也可以为委托进行赋值,但这样旧的委托引用会被GC回收掉。

delVar = new MyDel(SomeClass.Method1);//创建一个新委托对象
delVar = OtherClass.Method2; //赋值后,之前的Method1的引用被Method2覆盖掉了

5)为委托对象增加或移除其他的方法

//增加方法
delVar += someMethod1;
delVar += someMethod2; //移除方法
delVar -= someMethod1;

6)组合委托。可以将两个委托组合生成成一个新的委托,这个新的委托的调用列表连接了其他两个委托的调用列表副本。

MyDel delA = someClass.Method1;
MyDel delB = otherClass.Method2; MyDel delC = delA + delB;

7)调用委托。调用委托跟调用函数方法一样,参数必须同其调用列表中的方法一致。调用委托时会执行它的调用列表中的所有方法。(注:调用时委托不能为空)

delVar(Parameters);

其他注意事项:1.调用带返回值的委托时,委托的返回值为其调用列表中最后一个方法的返回值。2.调用带引用参数的委托时,参数会根据调用列表中的一个或多个方法的返回值二改变。

 什么是事件?

一句话概括,事件就好像是专门用于某种特殊用途的简单委托的封装。或者说,事件包含了一个私有的委托(也就是说你无法直接访问事件的委托)。

另外,事件中可用的操作比委托要少,对于事件我们只可以添加、删除或调用事件处理程序。事件被触发时,它调用对应的委托来依次调用调用列表中的方法。

对于事件的使用,.Net框架提供了一种标准模式,它定义了一种标准的委托类型EventHandler

EventHandler声明如下:

public delegate void EventHandler(object sender,EventArgs e)
// sender保存的是触发事件的引用,因为是object,所以可以匹配任何类型的实例 (可以使用as运算符拆箱转换)
// EventArgs是所有事件信息的基类,你可以自己声明一个继承自EventArgs的事件信息类来保存一些需要传递的数据信息。

声明EventHandler对应的事件:

public event EventHandler someEvent;

注:你可以按照 delegate void XXXXHandler(object sender,EventArgs e)的格式声明自定义的标准委托类型。

增加移除事件处理程序和委托类似,这里就不再赘述。

说了这么多,最好亲自动手实践一下,这样才能巩固自己学习的知识。

一个小小的应用例子

LOL是现在许多人(包括我)十分喜爱的一款很流行的5v5多人游戏,这个游戏里面的一方中5个人按职责划分为上单、打野、中单、ADC、辅助。这里主要是通过打野和上单的一个简单互动来说明一下事件的触发机制。具体的代码如下:

 using System;
using System.Threading; namespace EventStudy
{ public class Top //事件发布者(上单)
{
public string Hero { get; set; } //使用的英雄
public int hp { get; set; } = ; //英雄生命值
public delegate void LowHphandler(object sender, LowHpEventArgs e); //声明一个微软标准类型的委托
public event LowHphandler LowHpEvent; //声明一个该委托类型的事件 public class LowHpEventArgs : EventArgs //低生命事件信息
{
public readonly string LowHpHero;
public int restHp { get; set; }
public LowHpEventArgs(string hero, int _resthp)
{
LowHpHero = hero;
restHp = _resthp;
}
} public void BackToBase() //回城补给
{
Console.WriteLine("{0}回城了",Hero);
hp = ;
Thread.Sleep();
Console.WriteLine("他传送回了上路");
} public Top(string hero) //构造函数
{
Hero = hero;
} public void fight(int battleCounts )//战斗 battleCounts = 战斗次数
{
for(int i = ; i < battleCounts;i++) //战斗流程
{
Console.WriteLine("我方上单{0}正在与对方上单激烈对线中......",Hero);
Thread.Sleep();
hp -= ;
Console.WriteLine("交战后剩余生命值:{0}", hp);
Thread.Sleep();
if(hp <= ) //当生命值小于等于100时
{
if (LowHpEvent != null) //如果有对象注册
LowHpEvent(this, new LowHpEventArgs(Hero, hp)); //触发事件
else
Console.WriteLine("{0}说:草,没人关注上路啊?",Hero);
break;
}
}
} } class Jungle //订阅者(打野)
{
public string HeroName; //使用的英雄名字
public Jungle(string name)//构造函数
{
HeroName = name;
}
public void Help(object sender,Top.LowHpEventArgs e)//回调函数,或者说事件处理程序
{
Console.WriteLine("{0}说:卧槽,我们的上单{1}只有:{2}血了,我得去帮他",HeroName, e.LowHpHero, e.restHp);
}
} class Program
{
static void Main(string[] args)
{
//创建一个上单和一个打野
Top Yassuo = new Top("疾风剑豪-亚索");
Jungle Leesin = new Jungle("盲僧-李青"); Yassuo.LowHpEvent += Leesin.Help; //李青开始关注着亚索的生命情况
Yassuo.fight(); //亚索开始和对面对线 当达到特定情况时触发事件,然后执行对应的委托(即回调已经注册了的李青的Help方法)
Yassuo.BackToBase(); //亚索血量过低回城了 重置hp为500
Yassuo.LowHpEvent -= Leesin.Help; //亚索回城了,李青不再关注
Yassuo.fight(); //亚索继续和对面对线 当达到特定情况时又触发事件,但李青已经取消关注,事件处理为空,所以直接打印亚索要说的话
Console.ReadLine(); //Pause
}
}
}

对应的控制台输出如下:

这里概括来说主要就是盲僧的Help方法注册了亚索的低血量事件lowHpEvent,当亚索低血量时触发了事件,然后通过相应的的委托回调了盲僧的Help方法。亚索回城后,盲仔取消了对亚索低血量事件的关注。所以之后亚索再次低血量时,因为没有对应的事件处理程序(LowHpEvent == null),所以亚索开始吐槽没人来上路。

参考的相关资料:

C#图解教程(第四版) 作者:[美]Daniel M.Solis

大白话系列之C#委托与事件 :http://www.cnblogs.com/wudiwushen/archive/2010/04/20/1703368.html   作者:波哥2010

知乎回调函数相关: https://www.zhihu.com/question/1980113 。

C#学习(一):委托和事件的更多相关文章

  1. C#学习之委托和事件

    C#学习中,关于委托和事件的一些见解: 一.C语言中的函数指针 想要理解什么是委托,就要先理解函数指针的概念.所谓函数指针,就是指向函数的指针(等于没说-.-).比如我定义了两个函数square和cu ...

  2. C#学习之委托与事件

    委托 语法:  public  delegate void MyDelegate(); 使用:  1.定义委托----public  delegate void MyDelegate(); 2.注册委 ...

  3. .net学习之委托和事件

    1.什么是委托通俗的说:委托就是一个能够存储符合某种格式(方法签名)的方法的指针的容器上传图片: 2.委托语法准备一个方法:string Hello(string userName){} string ...

  4. .NET委托和事件

    .net学习之委托和事件   1.什么是委托 通俗的说:委托就是一个能够存储符合某种格式(方法签名)的方法的指针的容器 上传图片: 2.委托语法 准备一个方法:string Hello(string ...

  5. C#学习之初步理解委托、事件、匿名方法和Lambda

    最经在学习LinqtoSql,然后扯到Lambda表达式,然后扯到匿名方法,然后扯到委托,最后扯到事件处理...后来发现对委托这个概念和事件处理这个过程理解得不是很清晰,遂得一下学习笔记.那里说得不对 ...

  6. C#委托与事件学习笔记

    委托事件学习笔记 本文是学习委托和事件的笔记,水平有限,如有错漏之处,还望大神不吝赐教. 什么是委托?从字面意思来解释,就是把一个动作交给别人去执行.在实际开发中最常用的就是使一个方法可以当做一个参数 ...

  7. C#委托,事件最初浅的和最易看懂的学习笔记

    对于委托和事件,看了不少博文,当时好像都理解了,过了一段时间,又忘记的差不多了.每每如此,感觉自己很笨,记性差,其实是没有深入理解透切,没有按照自己的语言表达出来,当然容易忘记.今天又花了一些时间,好 ...

  8. C#高级编程 (第六版) 学习 第七章:委托和事件

    第七章 委托和事件 回调(callback)函数是Windows编程的一个重要方面,实际上是方法调用的指针,也称为函数指针. .Net以委托的形式实现了函数指针的概念,.Net的委托是类型安全的. 委 ...

  9. c#学习笔记03——委托和事件

    委托:一种引用类型,这种类型可以用来定义方法签名,从而使用委托实现将方法作为参数传递给其他方法.类似于C++中的函数之争,使用委托使程序员可以将方法引用封装在委托对象内. 定义和声明委托: deleg ...

随机推荐

  1. markdown简易快速的编辑格式(易读易写)

    实现简单快速书写,格式指定简便.易读易写 讲解http://wowubuntu.com/markdown/ 简单使用的讲解:http://www.ituring.com.cn/article/23 代 ...

  2. 反对抄袭 正解spring的@Autowired 不要相信网上的错误版本

    首先,最重要的, @Autowired的就是用来来消除 set ,get方法. 有些介绍,如著名的马士兵,说要在set方法上进行注入.我当时就看不明白了,既然只取消了一个GET,这个@Autowire ...

  3. C语言函数strstr()分析及实现

    原型:char *strstr(const char *str1, const char *str2); #include<string.h> 找出str2字符串在str1字符串中第一次出 ...

  4. UE4 Hello World 创建第一个UE4工程

    首先先熟悉几个UE4常用的类 AGameMode(控制整个项目的逻辑) The GameMode defines the game being played. It governs thegame r ...

  5. MFC中使用SDL播放音频没有声音的解决方法

    本文所说的音频是指的纯音频,不包含视频的那种. 在控制台中使用SDL播放音频,一般情况下不会有问题. 但是在MFC中使用SDL播放音频的时候,会出现没有声音的情况.经过长时间探索,没有找到特别好的解决 ...

  6. Android中代码运行指定的Apk

    有时候,当我们编写自己的应用的时候,需要通过代码实现指定的apk,安装指定的主题,或者安装新的apk.可以通过以下方法实现: private void installAPK(String apkUrl ...

  7. TCP连接建立系列 — 服务端接收SYN段

    本文主要分析:服务器端接收到SYN包时的处理路径. 内核版本:3.6 Author:zhangskd @ csdn blog 接收入口 1. 状态为ESTABLISHED时,用tcp_rcv_esta ...

  8. MVVM With ReactiveCocoa让移动开发更简单

    作者:@雷纯锋2011 MVVM是一种软件架构模式,它是 Martin Fowler 的 Presentation Model 的一种变体,最先由微软的架构师 John Gossman 在 2005 ...

  9. C语言中如何写一个简单可移植而又足够随机的随机数生成器

    在C语言中标准库中的随机数产生函数的返回可能不是最优的,因为有些随机数生成器的低位并不随机,而另一些返回随机数的函数实现上又太复杂鸟.所以rand()%N并不是一个好方法,牛人给出的建议是使用: ra ...

  10. DB2常用命令小结

    PS:执行命令前需要进入DB2的账户下:su db2inst1 修改密码:更改相应的操作系统密码即可,windows上可以更改db2admin的密码,linux上更改db2inst1的密码即可,db2 ...