C#学习(一):委托和事件
预备知识
在学习委托和事件之前,我们需要知道的是,很多程序都有一个共同的需求,即当一个特定的程序事件发生时,程序的其他部分可以得到该事件已经发生的通知。
而发布者/订阅者模式可以满足这种需求。简单来说,在这种模式中,发布者定义了一系列程序的其他部分可能感兴趣的事件。其他类可以“注册”,以便再这些事件发生时发布者可以通知它们。这些订阅者类通过向发布者提供一个方法来“注册”以获取通知。当事件发生时,发布者“触发事件”,然后执行订阅者提交的所有事件。
注:由订阅者提供的方法称为回调方法,或者说回调函数,关于回调函数可以参考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#学习(一):委托和事件的更多相关文章
- C#学习之委托和事件
C#学习中,关于委托和事件的一些见解: 一.C语言中的函数指针 想要理解什么是委托,就要先理解函数指针的概念.所谓函数指针,就是指向函数的指针(等于没说-.-).比如我定义了两个函数square和cu ...
- C#学习之委托与事件
委托 语法: public delegate void MyDelegate(); 使用: 1.定义委托----public delegate void MyDelegate(); 2.注册委 ...
- .net学习之委托和事件
1.什么是委托通俗的说:委托就是一个能够存储符合某种格式(方法签名)的方法的指针的容器上传图片: 2.委托语法准备一个方法:string Hello(string userName){} string ...
- .NET委托和事件
.net学习之委托和事件 1.什么是委托 通俗的说:委托就是一个能够存储符合某种格式(方法签名)的方法的指针的容器 上传图片: 2.委托语法 准备一个方法:string Hello(string ...
- C#学习之初步理解委托、事件、匿名方法和Lambda
最经在学习LinqtoSql,然后扯到Lambda表达式,然后扯到匿名方法,然后扯到委托,最后扯到事件处理...后来发现对委托这个概念和事件处理这个过程理解得不是很清晰,遂得一下学习笔记.那里说得不对 ...
- C#委托与事件学习笔记
委托事件学习笔记 本文是学习委托和事件的笔记,水平有限,如有错漏之处,还望大神不吝赐教. 什么是委托?从字面意思来解释,就是把一个动作交给别人去执行.在实际开发中最常用的就是使一个方法可以当做一个参数 ...
- C#委托,事件最初浅的和最易看懂的学习笔记
对于委托和事件,看了不少博文,当时好像都理解了,过了一段时间,又忘记的差不多了.每每如此,感觉自己很笨,记性差,其实是没有深入理解透切,没有按照自己的语言表达出来,当然容易忘记.今天又花了一些时间,好 ...
- C#高级编程 (第六版) 学习 第七章:委托和事件
第七章 委托和事件 回调(callback)函数是Windows编程的一个重要方面,实际上是方法调用的指针,也称为函数指针. .Net以委托的形式实现了函数指针的概念,.Net的委托是类型安全的. 委 ...
- c#学习笔记03——委托和事件
委托:一种引用类型,这种类型可以用来定义方法签名,从而使用委托实现将方法作为参数传递给其他方法.类似于C++中的函数之争,使用委托使程序员可以将方法引用封装在委托对象内. 定义和声明委托: deleg ...
随机推荐
- 【翻译】Sencha Ext JS 5发布
原文:Announcing Sencha Ext JS 5 简介 我代表Sencha和整个Ext JS团队,很自豪的宣布,在今天,Sencha Ext JS 5发布了.Ext JS 5已经迈出了一大步 ...
- 【Visual C++】游戏编程学习笔记之八:鼠标输入消息(小demo)
本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder 微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.c ...
- tomcat中的线程问题2
最近在看线程的有关知识,碰到一个小问题,目前还没有解决,现记录下来. 如果在我们自己写的servlet里有成员变量,因为多线程的访问就会出现一些线程问题.这点大家都知道,我们看下面的例子. publi ...
- navicat里导入和导出.sql文件
一:Navicat导入MySQL的.sql文件. 打开连接connection,选择你要导入的数据库,双击打开(或者新建一个),然后右键该数据库,选"运行批次任务文件(execute sql ...
- 【编程练习】收集的一些c++代码片,算法排序,读文件,写日志,快速求积分等等
写日志: class LogFile { public: static LogFile &instance(); operator FILE *() const { return m_file ...
- How tomcat works 读书笔记十七 启动tomcat 上
一路跋山涉水,这是最后一章了. 关于tomcat的启动,有两个类,一个是Catalina类,一个是Bootstrap类. 理论上,两个类可以和到一起,但是为了支持多种运行模式,又把他们分开了. 为了让 ...
- how tomcat works 读书笔记 十一 StandWrapper 下
StandardWrapperValve StandardWrapperValve是StandardWrapper的基础阀,主要完成了三个工作. 1 调用StandardWrapper的allocat ...
- ruby中顶层定义的方法究竟放在哪里?
ruby中顶层(top level)中定义的方法放在main中,证明如下: self.private_methods(false) #IN TOP LEVEL 那么methods方法究竟是在哪定义的, ...
- 伸展树--java
文字转载自:http://www.cnblogs.com/vamei 代码转载自:http://www.blogjava.net/javacap/archive/2007/12/19/168627.h ...
- javascript学习(一)构建自己的JS库
库是一个饱受争议的热门话题.一种观点认为它是一种非常棒的工具,是任何开发者都不可或缺的:另一种观点则认为在不理解库的内部工作原理的情况下对库形成依赖,会助长懒惰的风气从而导致开发者素质下降 库是一个饱 ...