日期:2014-05-17  浏览次数:20890 次

浅析C#之——Observer, Delegate和event(2)
接下来说一下Delegate
先介绍一下Delegate类:
Delegate——委托,是C#中对于函数指针进行封装后的对象,因为C#和JAVA一样都不允许直接使用指针这种高效但危险的东西,所以就用Delegate对象将其封装了起来以增加其使用的安全性;此外,比起直接使用字段,使用对象会更为方便,因为有了对象的封装后,就可以在对象内部预设需要的方法,为外部的调用增添许多便利。
创建委托时,delegate修饰符使其后类型继承MulticastDelegate,该基类提供委托以赋值,添加委托,移除委托和调用委托方法等操作,具体更多功能可查阅MSDN。

先写一个关于使用Delegate的例子:delegate.cs
using System;

// 创建一个delegate
delegate 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#应该是认为函数指针在程序设计还中是一