读书笔记:理解委托与事件
时间:2011-06-01 来源:scorpioying
今天是儿童节,在这个普天同庆的日子里,祝所有的小朋友以及尚有一颗童真的心的大人们节日快乐吧。也祝自己节日快乐。
昨天晚上,我一直沉浸在委托和事件的漩涡里,放弃了一周更新两集的电视剧,经过了一晚上的洗脑,总算觉得他们不再那么虚幻,对于我这样一个初学者来说,没有深接触,一开始理解起来确实不是很容易,照着模板,写了代码,对于只学了三个月C#的我来说,实在谈不上对本质有什么见解,在这里,我只是记录一下我的读书笔记,同时恳请各位大人们多多指点。
说完了开场白,进入正题吧。
首先说委托,什么是delegate,委托从本质上来讲就是一个类型,跟class很像,比如说,我们可以把阿森纳,曼联,利物浦...抽象成一个叫做club的类,同样,也可以把散步,爬楼梯,跳绳...归结为一个叫做sportDelegate的委托。能抽象成一个class的都是一些具有内部共性的对象,而能归结为一个delegate的则是一些方法。
View Codeclass Program
{
public delegate void hobbyDelegate(string name);
public static void watchFootballMatch(string name)
{
Console.WriteLine(name + " likes watching football match");
}
public static void playRubikCube(string name)
{
Console.WriteLine(name + " likes playing Rubik's Cube");
}
public static void readBooks(string name)
{
Console.WriteLine(name + " likes reading books");
}
public static hobbyDelegate my_hobby;
static void Main(string[] args)
{
my_hobby = new hobbyDelegate(watchFootballMatch);
my_hobby += new hobbyDelegate(playRubikCube);
my_hobby += new hobbyDelegate(readBooks);
my_hobby("cloverying");
}
}
从上述代码中可以看出委托类似于class的影子,我们可以定义一系列跟委托有着相同签名的方法(相同的返回值类型,相同的参数列表),将这些方法当做参数来传递,所以,委托的作用就相当于一个函数指针。
public static void watchFootballMatch(string name)
{
Console.WriteLine(name + " likes watching football match");
}
将watchFootballMatch当做参数传递给hobbyDelegate构造器,当调用my_hobby时,同时也调用了watchFootballMatch。
同时可以看到,在main函数中,watchFootballMatch方法,playRubikCube方法和readBooks方法都绑定(+=)到了my_hobby上,这种将多个方法绑定到同一个委托变量的形式叫做多播委托。同时也形成了一个委托链。在程序执行时,依次顺序执行就OK了。
因此,上述代码将输出:
如果要解除同某一个方法的绑定,只要利用 -= 操作符就可以了。代码如下:
my_hobby -= new hobbyDelegate(readBooks);
执行了以上代码后,将不会输出 cloverying likes reading books
绑定和解除绑定的执行机制可以看一下IL代码:
//省略的部分
IL_0023: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine
(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)
//省略的部分
IL_0063: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove从IL码可以看出,绑定和解除绑定就是执行了Combine和Remove操作。
(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)
我对IL代码了解的不是很多,可以说皮毛都算不上,只能根据IL代码勉强看出具体的执行过程,所以就不从IL代码上来分析委托的构造函数以及回调方法这些东西了。但是我觉得多看看IL代码还是有好处的,可以对内部具体的执行机制有个本质上的理解。
了解了委托究竟是什么东西后,再来看看大名鼎鼎的匿名方法.
首先要说明的很重要的一点就是:我今年3月份开始学习C#,所以没那个运气经历.NET 1.X到.NET 2.0 到.NET 3.0的历史变迁,因此一上来,我就要学习匿名方法和Lambda表达式,在我还不知道委托是什么玩意的时候,这些术语就满篇飞了,因此我晕了好长时间。但我不能让自己一直晕下去,所以我要在痛苦中享受delegate的乐趣~~
首先来思考一个问题,为什么要使用匿名方法。任何东西的诞生都是有原因的,尤其像这么一个重要的东东。这个问题真的很纠结,对于我这样一个初学者来说,没有任何开发的经验,单纯从代码来看,我只看到了使用匿名方法更加简洁,更加好理解这一好处,在这里,恳请各位前辈给小的指点一下吧~~
以下是使用了匿名方法的代码
View Codeclass Program
{
public delegate void hobbyDelegate(string name);
static void Main(string[] args)
{
hobbyDelegate watchFootballMatch=delegate (string name)
{
Console.WriteLine (name + " likes watching football match");
};
hobbyDelegate readBooks = delegate(string name)
{
Console.WriteLine(name + " likes reading books");
};
string myname = "ying";
watchFootballMatch(myname);
readBooks(myname);
}
}
输出结果为:
很明显,这样写把方法和委托实例watchFootballMatch以及readBooks直接关联起来了,不用在每次都new一下委托了。
说到这里,可以结合示例代码理解一下匿名方法的定义了。
在Mark Michaelis的《C# 本质论》中,对匿名方法的定义说明如下:
所谓匿名方法,就是没有实际方法声明的委托实例,或者说,他们的定义是直接内嵌在代码中的。
上面的代码中,委托实例watchFootballMatch和readBooks事先并没有做任何声明,而是直接给出了他们的定义,这样做最直观的一点就是代码变得简洁了,而且更加易读,嗯,代码易读比什么都珍贵。
下面再来看看更简洁的Lambda表达式,先看代码
class Program
{
public delegate void hobbyDelegate(string name);
static void Main(string[] args)
{
hobbyDelegate watchFootballMatch = (string name) => Console.WriteLine(name + " likes watching football match");
string myname = "ying";
watchFootballMatch(myname);
}
}
Lambda表达式比匿名方法更加直接了,引入“=>”后,就这么直接写出了方法实现,比上一段代码更清晰了。
从.NET各个版本对委托写法的进化充分印证了一点,世界是“懒人”创造的,代码越来越简洁,越来越看着顺眼了。
说完了委托,再来看看委托的管家:事件event.
简单来说,事件就是对委托的封装,从而保证了委托的安全性。那么很容易理解,事件一定是建立在委托机制上的。
很据《你必须知道的.NET》中对事件的定义过程,我写了如下代码:
View Code
public class Hobby
{
//First,定义一个HobbyEventArgs,其作用是用于存放事件引发时向事件处理程序传递的状态信息
//其实就是一个事件参数类型
public class HobbyEventArgs : EventArgs
{
//定义了一个只读字段,一个构造函数
public readonly string str_name;
public HobbyEventArgs(string name)
{
this.str_name = name;
}
}
//second, 声明事件委托
//包含两个参数:事件发送者对象 事件参数类对象
public delegate void HobbyEventHandler(object sender, HobbyEventArgs e);
//注:这里有一个命名规范,事件委托都是以EventHandler结尾的
//third,定义事件成员,作用是进行外部绑定
public event HobbyEventHandler my_hobby;
//fourth, 提供受保护的虚方法,因为是virtual,自然可以由子类来override,这样做可以拒绝监视
protected virtual void AboutHobby(HobbyEventArgs e)
{
if (my_hobby != null)
{
my_hobby(this, e);
}
}
//fifth 调用该方法,从而得知hobby有哪些
public void HobbyIS(string Name)
{
HobbyEventArgs e = new HobbyEventArgs (Name);
AboutHobby(e); //通知所有的事件注册者
}
}
以上对事件的定义代码完全是参照《你必须知道的.NET》一书一步一步照搬下来的,具体每一步的作用注释中已经写得很清楚了。
有了事件的定义后,还需要一个事件的触发程序,不然你怎么去实现这个事件呢是吧。
View Codepublic class HobbyManager
{
//定义消息通知方法
public void watchFootballMatch(object sender, Hobby.HobbyEventArgs e)
{
Console.WriteLine(e.str_name + " likes watching football match");
}
public void readBooks(object sender, Hobby.HobbyEventArgs e)
{
Console.WriteLine(e.str_name + " likes reading books");
}
}
OK,这就是事件的触发者,watchFootballMatch & readBooks。
下面是事件处理程序
class Program
{
static void Main(string[] args)
{
Hobby myhobby = new Hobby();
HobbyManager manager = new HobbyManager();
myhobby.my_hobby += manager.watchFootballMatch;
myhobby.my_hobby += manager.readBooks;
myhobby.HobbyIS("ying");
}
}
输出结果如下:
说到这里,我想像我一样以前从来没有接触过委托和事件的孩子们大概能知道delegate和event表面上是个什么东西了吧,我写的这些,完全谈不上什么个人见解,我也没那么资格,只是记录一下自己的学习过程,也加深一下理解,遗憾的是我还没有应用过。要说简单接触也就是在应用控件的时候触发一个button事件,嗯,这算是一个事件的应用了。
昨天一个老师说,我目前会用而且不会用错就OK了,等到以后再了解背后的东西,其实说实话,我还真不清楚什么是背后的东西,我也不清楚自己怎么才算是会用,什么才不会用错,问一个同学,从来就没有使用过委托和事件,我真不知道目前的阶段学习这些有没有多余,当然多余是指定不会的,毕竟学了比不学强,我只是突然觉得有些迷茫了。。。