浅析C#之——Observer, Delegate和event(2)
接下来说一下Delegate
先介绍一下Delegate类:
Delegate——委托,是C#中对于函数指针进行封装后的对象,因为C#和JAVA一样都不允许直接使用指针这种高效但危险的东西,所以就用Delegate对象将其封装了起来以增加其使用的安全性;此外,比起直接使用字段,使用对象会更为方便,因为有了对象的封装后,就可以在对象内部预设需要的方法,为外部的调用增添许多便利。
创建委托时,delegate修饰符使其后类型继承MulticastDelegate,该基类提供委托以赋值,添加委托,移除委托和调用委托方法等操作,具体更多功能可查阅MSDN。
先写一个关于使用Delegate的例子:delegate.cs
using System;// 创建一个delegatedelegate int MyHandler(int d1, int d2); class Test{public static int Add(int x, int y){Console.WriteLine("Add has been invoked..");return x+y;}public int Minus(int x, int y){Console.WriteLine("Minus has been invoked..");return x-y;}public static void Main(){Console.WriteLine("mh1:=====================================");MyHandler mh1 = new MyHandler(Test.Add);//绑定静态方法;Console.WriteLine("Outcome1: "+ mh1(100, 200));//==>mh1.Invoke(100, 200); ==> {jump Test.Add;}Console.WriteLine();Console.WriteLine("mh2:=====================================");Test t = new Test();MyHandler mh2 = new MyHandler(t.Minus);//绑定动态方法,这里其实传了两个参数:t和&Minus;Console.WriteLine("Outcome2: "+ mh2(100, 50));Console.WriteLine();Console.WriteLine("mh3:=====================================");MyHandler mh3 = mh1 + mh2;// ;构建委托链;有序的;Console.WriteLine("Outcome3: "+ mh3(200, 100));//依次执行mh1,mh2;返回值是最后一个委托方法的返回值,在此为mh2的返回值;Console.WriteLine();Console.WriteLine("mh4:=====================================");MyHandler mh4 = mh1 + mh2 + mh3;Delegate[] mhs = mh4.GetInvocationList(); //使用Delegate的GetInvocationList()方法可以数组的形式获得委托链中的每一个委托,从而实现对于委托链的随机访问;Console.WriteLine(mhs);for(int i = 0; i<mhs.Length; i++) //遍历委托链;{MyHandler tempHandler =(MyHandler)mhs[i];Console.WriteLine("outcome4: " + i + " " + tempHandler(200, 100));}Console.WriteLine();Console.WriteLine("mh5:=====================================");MyHandler mh5 = delegate(int x, int y){ //绑定匿名方法;Console.WriteLine("匿名方法:");return x*y;};Console.WriteLine("Outcome5: " + mh5(100,200));}}
输出结果:

以上是委托创建申明方法和delegate所支持的一些操作用法。
接下来想讨论一下delegate修饰符和Delegate类的内部实现,其实刚才的代码中的一些注释已经给出了答案:
我们可以通过使用ILDasm.exe查看实际生成的程序集:

由此可知,当编译器看到
delegate int MyHandler(int d1, int d2);
这行代码时,会将其转换为如下代码:
class MyHandler : MulticastDelegate{//构造器;public MyHandler(Object object, IntPtr method);//这个方法和源代码指定的原型一样;public virtual void Invoke(int d1, int d2);//以下方法实现了对回调方法的异步回调,具体还没有研究过。。。public virtual IAsyncResult BeginInvoke(...);public irtual void EndInvoke(...);} //MulticastDelegate内最主要的是包含了三个静态字段:class MulticastDelegate{private Object _target; //这个字段用来指出要传给实例方法的隐式参数this的值;//当委托对象包装的是一个静态方法时,这个字段为null;//当委托对象包装一个实例方法时,这个字段引用的是回调函数要操作的对象;private IntPtr _method;//一个内部的整数值,CLR用它标示要回调的方法;可以理解为一个函数指针;private Object _invocationList;//用来指向委托链中的下一个委托对象;//...}
当使用委托调用某方法时,如:
Console.WriteLine("mh2:=====================================");Test t = new Test();MyHandler mh2 = new MyHandler(t.Minus);//绑定动态方法,这里其实传了两个参数:t和&Minus;Console.WriteLine("Outcome2: "+ mh2(100, 50));Console.WriteLine();
它所生成的真正的程序集为:

由此可知,程序会在mh2被创建的时候将对象t赋给_target,将Minus的地址赋给_methodPtr以完成委托对象与方法的绑定;随后当执行到mh2(100, 50)时,因为编译器知道mh2是一个委托,所以它会事先将其自动转化为
mh2.Invoke(100,50)
从而调用mh2.Invoke(100,50)方法并根据先前保存下来的_target和_methodPtr跳转进入t.Minus()方法,执行t.Minus(100,200)。
以上,便是委托的事实真相!
接下来说一下我对于C#定义委托这一对象的理解:
Java中没有委托这个概念,但似乎可以用继承Handler接口来实现委托的功能,具体是怎么做的没有去研究。
总之,C#应该是认为函数指针在程序设计还中是一个很重要的概念,需要单独为其封装一个类并设定一些使用的机制。其实,或许大多数基于方法的晚绑定都可以用定义接口后将对象传入某方法随后调用接口中预设好的某个特定方法来实现(如之前用接口方法实现的Observer设计模式),只是这样的话就让人感觉很繁琐,不够直接。使用委托可以直接定位到对象中的某个方法,也能使代码更精简且利用阅读(可以对比一下之后用delegate和event实现的Observer设计模式)。
接下来用delegate改写先前的Observer.cs:Observer_delegate.cs
using System;//using System.Collections;interface ReceiveMessage{void storeMessage(String message);}//创建委托类型;delegate void ReceiverList(String message);class MessageCenter{private String messageString; // MessageCenter用来缓存消息的String字段;//private ArrayList receiverList = new ArrayList();private ReceiverList receiverList; //has a delegate;//添加消息接受者;public void addReceiver(ReceiveMessage receiver){//receiverList.Add(receiver);receiverList += new ReceiverList(receiver.storeMessage);}//移除消息接受者;public void removeReceiver(ReceiveMessage receiver){//receiverList.Remove(receiver);receiverList -= new ReceiverList(receiver.storeMessage);}// 新建消息;public void newMessage(String message){Console.WriteLine("New message: " + message + "... [MessageCenter]" );this.messageString = message;}// 通知MessageStore有新消息,将其备份入MessageStore;//public void notice(MessageStore messageStore)/*public void notice(ReceiveMessage messageStore) //将对象参数类型换成接口类型;{//messageStore.MessageString = this.messageString;//Console.WriteLine("Message: '" + messageString + "' have been stored in MessageStore...");messageStore.storeMessage(this.messageString);}*/public void notice()//// 通知所有已注册的receiver有新消息,并调用其storeMessage方法;{/*foreach(Object receiver in receiverList){(receiver as ReceiveMessage).storeMessage(this.messageString);}*/this.receiverList(this.messageString);}}class MessageStore : ReceiveMessage{private String messageString;// MessageStore用户存储消息的String字段;// 其实应该是一个String容器用来存储多条消息,不过在此为了简单只放一条消息;public String MessageString{get{return messageString;}set{this.messageString = value;}}/*public void storeMessage(String message) { this.messageString = message; Console.WriteLine("Message: '" + this.messageString + "' have been stored in MessageStore..."); } */ public void storeMessage(String message) //实现接口方法storeMessage(String message); { this.messageString = message; Console.WriteLine("Message: '" + this.messageString + "' have been stored in MessageStore..."); }// 列出MessageStore中已存储的消息;public void listMessages(){Console.WriteLine("Stored message1: '" + this.messageString + "'...[MessageStore]");}}class MessageChecker : ReceiveMessage{private String checkResult;public void storeMessage(String message) //实现接口方法storeMessage(String message);{this.checkResult = "OK!";Console.WriteLine("The message: '" + message + "' has been checked...");}public void showCheckResult(){Console.WriteLine("The checkResule is: " + this.checkResult + " [MEssageChecker]");}}class Test{public static void Main(){MessageCenter messageCenter = new MessageCenter();MessageStore messageStore = new MessageStore();MessageChecker messageChecker = new MessageChecker();messageCenter.addReceiver(messageStore);messageCenter.addReceiver(messageChecker);messageCenter.newMessage("Hello world!"); //新建消息Hello world;//messageCenter.notice(messageStore);//通知messageStore有消息需要存储;messageCenter.notice();messageStore.listMessages();//列出messageStore中已经存储的消息;messageChecker.showCheckResult(); //显示messageChecker的checkResule;}}
输出结果:

可以看到,就这样简单的用delegate改写一下也能使调用变得更方便一些,也使代码看起来更为简洁,但其实我们还可以做的更多。
================================================================================