喜羊羊与灰太狼之委托与事件
来源:互联网 发布:中国网络作家排行榜 编辑:程序博客网 时间:2024/04/29 12:40
记得我在初学.NET的时候对委托和事件这对概念理解的是很模糊的,当时在看书的时候只能理解书中代码的逻辑,知道委托和事件怎样用代码具体实现,但对其中的原理理解甚少。这几天在学习ASP.NET网页编程的时候,里面又多次提到了事件、事件参数。于是我决定再重新理解一次委托和事件
为了形象的描述委托和事件实现的过程,我想到了“喜羊羊和灰太狼”的故事。我比较爱看动画片,尤其是爱看像《喜羊羊与灰太狼》、《蜡笔小新》等这样既搞笑又弱智的动画片。没看过《喜羊羊与灰太狼》的可以趁此机会看一下。
为了描述这个故事,我们首先需要建立两个类:Goat和Wolf
//狼类的代码public class Wolf { string name; //定义一个变量用于存储狼的姓名 //构造函数 public Wolf(string name) { this.name = name; } public string Name { get { return name; } set { name = value; } } public void Scare() //狼有一个恐吓的方法 { Console.WriteLine("哈哈,我是{0},小肥羊们快跟我回狼堡吧!",name ); Console.WriteLine(); } }//山羊类的代码public class Goat { string name; //定义一个变量用于存储山羊的名字 //构造函数 public Goat(string name) { this.name = name; } public string Name { get { return name; } set { name = value; } } public void Run() //羊有一个逃跑的方法 { Console.WriteLine("狼来啦,{0}快跑!" ,name); Console.WriteLine(); } }
在动画片里,灰太狼每次看到小羊的时候都是先和小羊聊天、说一些吓唬小羊的话,然后再上去抓羊,这时候喜羊羊早就想出逃脱的办法了,怪不得每次灰太狼都抓不到羊,看到羊先和他们扯淡,然后才行动。
不说废话了,下面我给大家讲怎样用委托和事件实现:狼吓唬小羊,小羊听到后撒腿就跑。
首先我们要明白一个常识:灰太狼再笨也不会自己告诉小羊说我来了,你们快跑吧。所以在Wolf类当中不可以出现Goat类的对象,也就是说不可以把小羊的Run方法写在狼的Scare方法中。Wolf和Goat双方不能关联,那就要通过委托来解决这个问题
委托其实就是对方法的引用,一旦为委托分配了方法,那么委托就可以像方法一样使用。委托定义了对方法特征的抽象,委托和函数一样可以有参数和返回值,为委托分配的方法的格式要和委托一致,即方法的参数和返回值要和委托一样。委托可以看做是函数的“类”,为委托分配的函数就是委托的实例。
首先我们定义一个委托
public delegate void WolfScareEventHandler();
定义一个没有参数和返回值的委托,委托的名字是WolfScareEventHandler,委托用关键字delegate声明,可以放在Wolf类的里面,也可以放在类外面,因为委托本身也是一种“特殊的类”
然后我们定义一个WolfScareEventHandler委托类型的事件WolfScare
public event WolfScareEventHandler WolfScare;
事件用Event关键字声明,事件要声明在Wolf类的内部,因为这个事件是属于Wolf类的(我们通常说类有三要素:属性、事件、方法)
包含WolfScare事件的Wolf类代码
public class Wolf { string name; //构造函数 public Wolf(string name) { this.name = name; } public string Name { get { return name; } set { name = value; } } //声明一个WolfScareEventHandler委托类型的事件 public event WolfScareEventHandler WolfScare; public void Scare() { Console.WriteLine("哈哈,我是{0},小肥羊们快跟我回狼堡吧!",name ); //下面是触发WolfScare事件的代码 if (WolfScare != null) { WolfScare(); } } }
这样Wolf类就具有了事件,而且在狼执行Scare方法时事件会被触发,如果为事件分配了方法,那么将执行方法。
最关键的是要看主程序的代码:
static void Main(string[] args) { Wolf bigWolf = new Wolf("灰太狼"); //声明一个Wolf类的对象bigWolf,名字是“灰太狼” //声明三个Goat对象 Goat happyGoat = new Goat("喜羊羊"); Goat lazyGoat = new Goat("懒羊羊"); Goat strongGoat = new Goat("沸羊羊"); //给事件分配方法 bigWolf.WolfScare += new WolfScareEventHandler(happyGoat .Run ); bigWolf.WolfScare += new WolfScareEventHandler(lazyGoat.Run); bigWolf.WolfScare += new WolfScareEventHandler(strongGoat.Run); bigWolf.Scare (); }
在主程序当中,为bigWolf对象的事件WolfScare分配了三个方法:happyGoat .Run 、lazyGoat.Run、strongGoat.Run。用委托的好处就在这里,不用在Wolf类当中出现Goat类,而是在主程序当中为事件动态分配方法,而且这些方法可以是不同对象的方法,只要格式和委托类型一致就可以了。
这样在bigWolf执行Scare方法时,事件WolfScare会被触发,同时执行happyGoat .Run 、lazyGoat.Run、strongGoat.Run三个方法,运行结果如下
有人会说.NET中控件事件过程一般都是带参数的,像下面这样:
protected void Button1_Click(object sender, EventArgs e) { }
这个事件过程带两个参数sender和e,分别是object类型和EventArgs类型的。sender是指此事件是在哪个对象中被触发的,在这里就是Button1;e是一个事件参数,里面携带着一些与此事件相关的数据
那么我们对“喜羊羊和灰太狼”的代码进行改造,让它的事件也带参数
先定义一个事件参数类WolfScareEventArgs,让它继承EventArgs类
public class WolfScareEventArgs { string name; //定义一个name变量,让它存储狼的名字,这样事件参数就可以携带狼的名字了 public string Name { get { return name; } set { name = value; } } }
对委托改造:事件过程(即为委托分配的方法)是带参数的,那么委托也要带参数
public delegate void WolfScareEventHandler(object sender,WolfScareEventArgs e);
改造后的Wolf类
public class Wolf { string name; //构造函数 public Wolf(string name) { this.name = name; } public string Name { get { return name; } set { name = value; } } //声明一个WolfScareEventHandler委托类型的事件 public event WolfScareEventHandler WolfScare; public void Scare() { Console.WriteLine("哈哈,我是{0},小肥羊们快跟我回狼堡吧!",name ); Console.WriteLine(); //下面是触发WolfScare事件的代码 if (WolfScare != null) { //定义一个事件参数对象 WolfScareEventArgs args = new WolfScareEventArgs(); args.Name = this.name; //将狼的名字赋给事件参数的Name属性 WolfScare(this,args ); } //重写ToString方法,这样sender.tostring()就可以显示狼的名字了 public override string ToString() { return this .name ; } }
Goat类的代码也要进行改造:
public class Goat { string name; //构造函数 public Goat(string name) { this.name = name; } public string Name { get { return name; } set { name = value; } } public void Run(object sender,WolfScareEventArgs e) { Console.WriteLine("{0}来啦,{1}快跑!",e.Name ,name); //在这里可以用参数e的Name属性显示出哪只狼来了 Console.WriteLine("追我的狼是:"+sender.ToString ()); //这里可以用sender对象的ToString方法显示出触发事件的狼的名字 Console.WriteLine(); } }
主程序基本不用变,为了方便演示,我们把狼的名字改为“红太狼”
static void Main(string[] args) { Wolf bigWolf = new Wolf("红太狼"); Goat happyGoat = new Goat("喜羊羊"); Goat lazyGoat = new Goat("懒羊羊"); Goat strongGoat = new Goat("沸羊羊"); //给事件添加方法 bigWolf.WolfScare += new WolfScareEventHandler(happyGoat .Run ); bigWolf.WolfScare += new WolfScareEventHandler(lazyGoat.Run); bigWolf.WolfScare += new WolfScareEventHandler(strongGoat.Run); bigWolf.Scare (); }
程序运行的效果:
讲到这里可能有很多人还是觉得上面讲的事件和.NET中的控件事件不太一样,其实他们的原理是一样的:
当我们双击某个控件时,代码编辑器会自动为我们编写好一个事件处理过程
protected void Button1_Click(object sender, EventArgs e) { }
其实这里的Button1_Click 就类似于上面讲到的Goat类的Run方法。Button1_Click过程的背后是事件Button1.Click,它类似于上面的bigWolf.WolfScare事件。
其实Button1_Click可以是随便的一个名字,比如 fun (object sender, EventArgs e),只要你在代码中添加如下语句:
Button1.Click+=New EventHandler(fun);
这样当Button1的Click事件被触发时,就会执行fun方法。你可以为Button1.Click事件分配任意多的方法,当事件被触发时,这些方法都会被执行。
你看到的事件过程都是类似于 protected void Button1_Click(object sender, EventArgs e)这样的,这是因为:当你双击某个控件时,系统自动创建过程protected void Button1_Click(object sender, EventArgs e),并将它分配给事件Button1.Click. 你也可以把其它控件的事件过程分配给事件Button1.Click,如:
protected void TextBox1_TextChanged(object sender, EventArgs e) { Response.Write("这是文本框的事件过程"); }Button1.Click+=New EventHandler(TextBox1_TextChanged)
这样单击Button1,TextBox1_TextChanged事件过程也会被执行。
总之事件过程和一般的函数和方法是一样的,只不过系统把它分配给了事件,当事件触发时,执行被分配的事件过程
事件的原理你明白了吗?
- 喜羊羊与灰太狼之委托与事件
- 喜羊羊与灰太狼之委托与事件
- 事件与委托之我见
- C#之委托与事件
- C#之委托与事件
- C#编程之委托与事件(二)
- C#编程之委托与事件(一)
- C#委托与事件之学习
- Unity 之C# 委托与事件机制
- 设计模式 之 观察者--委托与事件
- C#编程之委托与事件(一)
- C#编程之委托与事件(二)
- 委托入门-事件与委托
- 委托与事件
- 委托与事件
- c# 委托与事件
- 委托与事件
- 事件与委托详解
- 台积电于2016年10nm制程开始采用EUV技术
- 学习struts2建bbs总结四:解决jsp/action中文传值乱码问题
- 逐行向上滚动 js 代码,鼠标移上停止滚动,不满一屏的时候不滚动
- QWT编译
- NI与TU-Dresden联合研究第五代(5G)移动通信网络
- 喜羊羊与灰太狼之委托与事件
- c语言一些有用的函数
- 中兴通讯智能手机生产链探秘
- ZOJ 1082 (最短路径 Floyed)
- 仿QQ开发的某个阶段小结
- hdu 4362 Dragon Ball 很裸的DP
- Linux时间管理(四)
- UVA 10601 CUBES (正方体Polya,有限制)
- Qt下的OpenGL 编程(6)混合、雾、抗锯齿