委托浅析
1.概念
委托提供了类似C++中函数指针的功能,简单地说委托类型就是面向对象函数指针, 不过C++的函数指针只能够指向静态的方法, 而委托除了可以指向一个静态的方法之外还可以指向对象实例的方法,并且委托是完全面向对象且使用安全的类型。
2.本质
委托的本质其实就是一种数据类型,更确切地说,其实就是一类类。只是它比较特殊而已,这种数据类型的变量(或者说这种类的实例对象)的值只能接受一个函数(的地址)。
我们知道int和char都是一种数据类型,类(class)也是一种数据类型,只是int、char是值类型的,而类(class)是一种引用类型的。我们说的委托就是一种引用类型的,所以其本质就是类。
3.签名
虽然说委托类型变量的值是函数的地址(相当于函数指针),但并不是说委托类型变量可以指向任何函数,它所指向的函数必须跟它具有相同的签名。这里的签名是指返回类型和传入的参数,就像函数一样,委托也是有返回类型和传入参数的,那么它代理的函数必须跟它具有一样的签名。当然,与委托的签名匹配的任何方法都可以分配给该委托。
4.使用
我们知道委托本质就是类,那么它的使用跟类的使用差不多。先定义,然后实例化,然后赋值,最后就可以使用了。具体见例子。
5.好处
它可以指向一个静态的方法,也可以指向对象实例的方法,同时它是完全面向对象和类型安全的。
另外,委托允许程序设计师可以在执行时期传入方法的名称动态地决定欲调用的方法,灵活性和扩张性非常强。
再者,它可以实现委托链(多路广播)。
6.应用场景
委托一般在事件、多线程和通用类库设计中用的比较多。
下面列举了2个示例对委托的概念和基本用法进行说明(这些例子来自陈广老师和楚广明老师的课堂,自己稍作了修改和融合)
为了省去创建GUI那些繁琐的步骤,更清晰地逼近委托的本质,接下来的所有程序都是控制台程序,程序最后的Console.ReadKey()是为了使程序中途停下来,以便看清楚执行过程中的输出。
一、委托的基本使用示例
//一、委托的基本使用示例 namespace DelegateDemo { //委托的定义 委托的本质就是一个类,任何可以定义类的地方都可以定义委托 delegate void EatDelegate(string food);//定义时要注意其返回类型和参数要与被代理的方法的返回类型和参数一致,也就是签名要一致 class MyDelegate { static void ZsEat(string food)//被代理的方法,静态方法 { Console.WriteLine("张三吃:" + food); } static void LsEat(string food)//被代理的方法,静态方法 { Console.WriteLine("李四吃:" + food); } static void Main(string[] args) { //代理静态方法 EatDelegate zsEat = new EatDelegate(ZsEat);//委托的实例化,在委托实例化过程中就将要代理的函数名通过构造函数进行了赋值,这是给委托赋值的方式一 zsEat("西瓜");//委托的使用; EatDelegate lsEat = LsEat;//声明一个委托变量,同时给它赋值,直接将方法名传递给委托变量,这是给委托赋值的方式二 lsEat("西瓜"); //代理动态方法 Man ww = new Man("王五"); Man zl = new Man("赵六"); EatDelegate wwEat = new EatDelegate(ww.Eat); wwEat("西瓜"); EatDelegate zlEat = zl.Eat; zlEat("西瓜"); Console.WriteLine(); //下面是实现一个委托链 EatDelegate eatChain;//声明一个委托变量 Console.WriteLine("张三、李四、王五开座谈会"); eatChain = zsEat + lsEat + wwEat;//委托链(多路广播) eatChain("香蕉"); Console.WriteLine(); Console.WriteLine("李四出去接电话"); eatChain -= lsEat; eatChain("橘子"); Console.WriteLine(); Console.WriteLine("李四回来了"); eatChain += lsEat; eatChain("苹果"); Console.WriteLine(); Console.WriteLine("这时赵六来了"); eatChain += delegate(string food) { Console.WriteLine("赵六吃:" + food); };//注意:这里是委托匿名方法; eatChain("菠萝"); Console.ReadKey(); } } class Man { private string name; public Man(string name) { this.name = name; } public void Eat(string food)//被代理的方法,动态方法 { Console.WriteLine(name + "吃:" + food); } } }
输出结果:
二、代理作为方法参数的示例
//二、代理作为方法参数的示例 namespace DelegateDemo { class MyDelegate { public delegate int GenericFun(int a, int b); //实现加法 public static int Add(int a, int b) { return a + b; } //实现减法 public static int Sub(int a, int b) { return a - b; } //返回运算结果,这个地方将代理作为一个函数参数进行传递,这就是我们说的委托允许程序设计师可以在执行时期传入方法的名称动态地决定欲调用的方法,灵活性和扩张性非常强 public static int PrintResult(GenericFun action, int a, int b) { int result = action(a, b); return result; } static void Main(string[] args) { Console.WriteLine("3+4=" + PrintResult(Add, 3, 4));//打印3+4的结果 Console.WriteLine("3-4=" + PrintResult(Sub, 3, 4));//打印3-4的结果 Console.ReadKey(); } } }
输出结果:
3+4=7
3-4=-1