复习委托事件
人一安逸就不思进取,找打工作后就没有以前那么认真了,人也变懒了,用不着的不学不练。想学学构架方面的知识,看着看着就发现以前不少基本的东西都生疏了。比如看到观察者模式的时候就像不太清楚委托事件的知识了,所以找了点文章复习下。
委托是一种数据结构,是一种类型,引用类型,能够引用静态方法或者实例的方法,类似于C的函数指针,可以把方法当着参数传递,在很多地方避免了进行更过的判断。
事件是实例,其实是对委托的进一步封装,其实例化了一个私有的委托对象,通过add()和remove()来注册和注销事件,以防止在实例外部能主动的触发注册的事件,而达到只能在事件的发布者内部决定是否要触发事件的目的。等于对委托的进一步阉割。
下面例子的环境是模拟用户订阅彩票发布机构,用户订阅彩票中奖号码发布机构的发布中奖号码这件事(就是订阅者对这件事情感情兴趣),当发布中奖号码事件触发,用户就能知道自己是否中奖。
彩票中奖信息发布结构。
//彩票中奖号码发布机构 public class LotteryPublisher { //声明发布中奖号码事件的委托 public delegate void PublisherHander(string _Number); //建立发布中奖号码事件 public event PublisherHander OnPublisher; //触发事件方法(发布中奖信息) public void Trigger() { string num = GetNumber(); if (OnPublisher != null) { Console.WriteLine("本期中奖号码为{0}", num); OnPublisher(num); } } //摇号(随机产生九位数字) private string GetNumber() { string num = ""; Random random = new Random(); for (int i = 0; i < 9; i++) { num += random.Next(9).ToString(); } return num; } }
购买彩票的用户。
public class Client { //客户姓名 private string name = ""; //客户自己选的号码 private string myNumber = ""; public Client(string number,string name) { this.myNumber = number; this.name = name; } //要注册的方法,查看自己是否中奖 public void Check(string number) { if (myNumber == number) { Console.WriteLine("{0}:哦也,我中奖了,哈哈哈!!!",name); } else { Console.WriteLine("{0}:TMD小日本,又没中~~~",name); } } }
接下来摇奖开始。
class Program { static void Main(string[] args) { LotteryPublisher pub = new LotteryPublisher();//实例化彩票摇奖器对象 Client client1 = new Client("9657412355","张三"); Client client2 = new Client("8516541558","李四"); pub.OnPublisher += new LotteryPublisher.PublisherHander(client1.Check); pub.OnPublisher += new LotteryPublisher.PublisherHander(client2.Check); //摇奖开始 Console.WriteLine("摇奖开始"); pub.Trigger(); Console.ReadKey(); } }
平时我们在网页上或者WinForm窗体上拖上一个button,然后双击,就能生成这样一段代码
private void button1_Click(object sender, EventArgs e) { //自定义的事件处理代码 }
我们可以看到其中有两个参数,分别是 sender和e,如果加个断点就能发现sernder就是触发事件的对象,这里也就那个button,而e就是事件感兴趣的对象,这里就是button的X,Y坐标之类的。接下来说下微软对委托事件的.Net Framework的编码规范。
1、委托类型的名称都应该以EventHandler结束。
2、委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object 类型,一个 EventArgs类型(或继承自EventArgs)。
3、事件的命名为 委托去掉 EventHandler之后剩余的部分。
4、继承自EventArgs的类型应该以EventArgs结尾。
我们现在按照微软的编码规范再改造下我们的程序
订阅者所感兴趣的事情(摇奖产生的中奖号码)
//订阅者所感兴趣的事情(摇奖产生的中奖号码) public class NumberEventArgs : EventArgs { //摇奖产生的中奖号码 private string number; public string Number { get { return number; } set { number = value; } } public NumberEventArgs(string _number) { Number = _number; } }
彩票中奖号码发布机构。
public class LotteryPublisher { //声明发布中奖号码事件的委托 public delegate void PublisherEventHander(object sender,NumberEventArgs e); //建立发布中奖号码事件 public event PublisherEventHander Publisher; //触发事件方法(发布中奖信息) protected void OnPublisher(NumberEventArgs e) { if (Publisher != null) { Console.WriteLine("本期中奖号码为{0}", e.Number); Publisher(this,e ); } } public void Trigger() { OnPublisher(new NumberEventArgs(GetNumber())); } //摇号(随机产生九位数字) private string GetNumber() { string num = ""; Random random = new Random(); for (int i = 0; i < 9; i++) { num += random.Next(9).ToString(); } return num; } }
购买彩票客户。
//购买彩票客户 public class Client { //客户姓名 private string name = ""; //客户自己选的号码 private string myNumber = ""; public Client(string number, string name) { this.myNumber = number; this.name = name; } //要注册的方法,查看自己是否中奖 public void Check(object sender, NumberEventArgs e) { if (myNumber == e.Number) { Console.WriteLine("{0}:哦也,我中奖了,哈哈哈!!!", name); } else { Console.WriteLine("{0}:又没中~~~", name); } } }
最后开始摇奖。
static void Main(string[] args) { LotteryPublisher pub = new LotteryPublisher();//实例化彩票摇奖器对象 Client client1 = new Client("9657412355","张三"); Client client2 = new Client("8516541558","李四"); pub.Publisher += new LotteryPublisher.PublisherEventHander(client1.Check); pub.Publisher += new LotteryPublisher.PublisherEventHander(client2.Check); //摇奖开始 Console.WriteLine("摇奖开始"); pub.Trigger(); Console.ReadKey(); }
有了委托机制我们就能把方法当成参数来传递,而委托和事件结合,我们就能再出事件发布者内部定义好特定的逻辑,而在这逻辑中细节实现依赖于事件订阅者的处理逻辑,当我们把事件订阅者处理逻辑的方法以注册事件的方法(就是传递方法的委托)传递到事件订阅者内部,就能实现我们的整个功能。
现在看看上面的代码有没有一种很熟悉的感觉~~,其实.net中委托事件是对观察者模式的一种原生支持,其背后就是观察者模式(Observer)。