入行半年多了,委托干什么用的还不知道,真心说不过去了,关键对这东西有点恐惧,主要是被老师吓的,记得我C#专业课老师在讲到委托时,原话是这样的,同学们,委托这个地方是难点,暂时不讲,讲了你也不懂,等你有了一定的编程经验,你自会明白。好吧,我懂了,只可意会不可言传。其实,它并没有想象中这么恐怖,只不过我被吓着了,向来就胆小,不敢挑战有点难度的东西,只能玩玩表面的东西,嗨,不废话了,但是这是按照博客园惯例来的,一开始不能太专业,专业了就不是“面向对象”了。
好吧,先提个问题,委托到底是什么?这是个头脑风暴,那家可以随便想.......是类型?是方法?还是是对象? 大家有没有说说你的见解的,大多数教程里更愿意把它叫做代理,叫做方法的指针,好吧,又专业了。其实我认为,这几种的叫法都合适。那它到底是什么?有什么用?为了让不懂编程的人也能看懂我写的东西,我举个实际生活中的例子,比如,二狗,我今天有点事,孩子放学回家没人做饭,我今天就把他暂时交给你照顾了,好吗?那这里做饭这件事其实就是C#中的方法,一个事件的处理过程,二狗就是你的委托人,本来你是孩子的监护人,你理应直接照顾孩子的吃饭上学等等,但是今天特殊情况,自己实在做不了,你就把这事暂时委托给你的朋友,让他帮一下吗。相信大家懂了吗,有没有稍微懂,稍微懂就够了,我们可以跳到C#里来,重新讲述一下委托这个概念,C#中,你本可以直接操作方法,你是方法的创造者(自定义方法),或者,C#为你做好的方法(类库里的方法),但是我把方法委托给别人来做,但是这个人要有和我一样的能耐(可以为理解方法签名要一致)才能帮我完成方法的调用。
补充方法签名的概念,方法签名指方法名称、参数个数、参数类型、方法参数的顺序。与方法返回值无关。方法签名也成为方法标识。
TIP:"在方法重载的上下文中,方法的签名不包括返回值。 但在委托的上下文中,签名包括返回值。 换句话说,方法和委托必须具有相同的返回类型。"来自MSDN
接下来我们按照委托如何定义,如何使用,为什么要使用委托几个角度学习它。并与C语言中函数指针类比学习。
以实现四则计算器为目的,首先定义加、减、乘、除四个计算方法,在主函数中使用委托的方式进行调用。
class Program { delegate int MathDelegate(int i, int j);//delegate 和 Delegate 的区别是什么 static int Add(int i,int j) { return i+j; } static int Subtract(int i,int j) { return i - j; } static int multiply(int i,int j) { return i * j; } static int devide(int i,int j) { return i / j; } static void Main(string[] args) { MathDelegate myDelegate = new MathDelegate(Add); int result=myDelegate(10,20); Console.WriteLine("加法结果是:{0}",result); myDelegate = new MathDelegate(Subtract); result = myDelegate(10, 20); Console.WriteLine("减法结果是:{0}", result); myDelegate = new MathDelegate(multiply); result = myDelegate(10, 20); Console.WriteLine("乘法结果是:{0}", result); myDelegate = new MathDelegate(devide); result = myDelegate(10, 20); Console.WriteLine("除法结果是:{0}", result); Console.ReadKey(); } }
如同i可以接受int类型的“1”,但不能接受bool类型的1一样。MathDelegata的 参数类型定义 应该能够确定 MathDelegata可以代表的方法种类,再进一步讲,就是MathDelegata可以代表的方法 的 参数类型和返回类型,就是方法签名的意思。
于是,委托出现了:它定义了MathDelegata参数所能代表的方法的种类,也就是MathDelegata参数的类型。
NOTE:如果上面这句话比较绕口,我把它翻译成这样:string 定义了name参数所能代表的值的种类,也就是name参数的类型。
static int Count(int i, int j, MathDelegate myDelegata) { return myDelegata(i,j); }
如你所见,委托MathDelegata出现的位置与 int相同,int是一个类型,那么MathDelegata应该也是一个类型,或者叫类(Class)。但是委托的声明方式和类却完全不同,这是怎么一回事?实际上,委托在编译的时候确实会编译成类。因为Delegate是一个类,所以在任何可以声明类的地方都可以声明委托。更多的内容将在下面讲述,现在,请看看这个范例的完整代码:
class Program { delegate int MathDelegate(int i, int j);//delegate 和 Delegate 的区别是什么 static int Add(int i,int j) { return i+j; } static int Subtract(int i,int j) { return i - j; } static int Multiply(int i,int j) { return i * j; } static int Devide(int i,int j) { return i / j; } static int Count(int i, int j, MathDelegate myDelegata) { return myDelegata(i,j); } static void Main(string[] args) { int result = Count(20, 10, Add); Console.WriteLine("加法结果是:{0}",result); result = Count(10, 20,Subtract); Console.WriteLine("减法结果是:{0}", result); result = Count(10, 20,Multiply); Console.WriteLine("乘法结果是:{0}", result); result = Count(10, 20, Devide); Console.WriteLine("除法结果是:{0}", result); Console.ReadKey(); }
我们现在对委托做一个总结:
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
说到这里我们已然可以把委托理解为一个类型或一个类,就想String一样,但是委托有一个特性不同于类型:可以将多个方法赋给同一个委托,或者叫将多个方法绑定到同一个委托,当调用这个委托的时候,将依次调用其所绑定的方法。我们称它为多播委托,在这个例子中,语法如下:
class Program { delegate int MathDelegate(int i, int j);//delegate 和 Delegate 的区别是什么 static int Add(int i,int j) { Console.WriteLine("加法"); return i+j; } static int Subtract(int i,int j) { Console.WriteLine("减法"); return i - j; } static int Multiply(int i,int j) { Console.WriteLine("乘法"); return i * j; } static int Devide(int i,int j) { Console.WriteLine("除法"); return i / j; } static int Count(int i, int j, MathDelegate myDelegata) { return myDelegata(i,j); } static void Main(string[] args) { //多播委托 MathDelegate myDelegate = new MathDelegate(Add); myDelegate += Subtract; myDelegate += Multiply; myDelegate += Devide; int result = Count(10, 20, myDelegate); Console.WriteLine("结果是:{0}", result); Console.ReadKey(); } }
运行结果为
由运行结果分析,当一个委托引用同时指向多个符合方法标识的方法时,此时即构成多播委托。其本质是对System.MulticastDelegate类(为System.Delegate的子类)的继承和实现。由于一个委托只能有一个返回值,故一般多播委托为void的(并非一定),若有返回值,则最终的返回结果及多播系列中最后一个方法的返回值。
当多播委托的引用只指向一个方法,则其使用方法与单播委托一样,但是若指向多个方法,则除第一个使用=,其他均用+=。注意这里,第一次用的“=”,是赋值的语法;第二次,用的是“+=”,是绑定的语法。如果第一次就使用“+=”,将出现“使用了未赋值的局部变量”的编译错误。例如:
MathDelegate myDelegate = new MathDelegate(Add); myDelegate += Subtract; myDelegate += Multiply; myDelegate += Devide;
既然一个委托可以绑定方法,就有办法取消对方法的绑定,很容易想到这个语法:-=
//第三种方式:多播委托 MathDelegate myDelegate = new MathDelegate(Add); myDelegate += Subtract; myDelegate += Multiply; myDelegate += Devide; myDelegate -= Devide;//注意这里的写法
由上个例子改写运行结果:
我们看到除法没有了,是因为解除了对除法的绑定,最后一个方法是乘法,返回的结果是最后一个方法的运算结果。