喜羊羊与灰太狼之委托与事件

来源:互联网 发布:中国网络作家排行榜 编辑:程序博客网 时间: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事件过程也会被执行。
       总之事件过程和一般的函数和方法是一样的,只不过系统把它分配给了事件,当事件触发时,执行被分配的事件过程


事件的原理你明白了吗?


原创粉丝点击