注:以下文章仅为学习笔记,很大部分来自于他人博客或资料,我会相应注明,仅为学习。
一、为什么要有委托
要学习委托,我们首先要明白为什么要有委托,委托的意义在于什么。
下面是来自于黑马论坛上某人的通俗解说,我觉得很好:
1,当你需要把一个方法传送给其他方法时,可以考虑使用委托。好像不是很好理解;
2,也可以这样说,当你确定要处理一件事,但又不能确定处理方法时,可以考虑用委托。
3,其实单独的说委托的应用好像有点牵强,委托更多的是在事件中的应用。
4,举个例子吧,目的是理解委托的原理,
[
你想要吃饭,
但是自己又不会做;//(委托方不知道实现细节),
你计划找个小吃店,叫个西红柿牛腩盖饭;//(定义了一个委托)
你决定找常去的那家叫做XXX的小吃店(实例化一个委托)
你打电话给XXX小吃店;//(委托调用)
XXX小吃店给你做好了你定的西红柿牛腩;//(代理函数工作)
饭来了,真好。//委托执行结束
]
我在博客园上看到一个引入委托的例子,很形象,讲的是主管监视员工是否玩游戏的故事,非常好的说明了委托的好处,这里我将举另一个例子,这个就大家自己去关注他的博客吧:http://www.cnblogs.com/superpcer/archive/2011/06/06/2073751.html
二、委托的好处(不谈事件)
1、将方法作为参数传递,降低代码间的耦合度
2、让代码更加的简单,降低复杂度,增加重用性。
3、委托可顺序的执行所封装的方法
二、委托的解释
委托(delegate)是一种包装方法调用的类型。就像类型一样,可以在方法之间传递委托实例,并且可以像方法一样调用委托实例。匿名函数是一个“内联”语句或表达式,可在需要委托类型的任何地方使用。可以使用匿名函数来初始化命名委托,或传递命名委托(而不是命名委托类型)作为方法参数。
三、实例
以下我用到的例子来自于:http://nt.discuz.net/showtopic-130142.html
下面的例子是对学生信息按照学生的不同属性进行排序。
首先我们新建一个Student类:
public class Student { //这里为了简便就不用属性了 public int id; public string name; public int age; public int classId;//假设有一个班级编号 public Student(int id, string name, int age, int classId) { this.id = id; this.name = name; this.age = age; this.classId = classId; } }
新建一张aspx的页面,拖一个按钮和一个Label,双击产生相应代码
"Button2" runat="server" Text="委托排序" onclick="Button2_Click" />
"Label2" runat="server" Text="">
在相应代码中new一些Student的数组成员出来
protected void Button2_Click(object sender, EventArgs e) { //声明一个Student的数组 //为什么不用List或者List呢,因为二者都是微软封装好的数据结构,实现了排序,没必要我画蛇添足 Student[] s ={ new Student(1004,"成成",19,4), new Student(1002,"张才",22,1), new Student(1003,"李大为",20,4), new Student(1005,"冰冰",23,5), new Student(1001,"李晓红",21,3), }; }
假设我们按照id来对s排序,写出冒泡排序法sort(Student[] s);
//实现冒泡排序法 private void Sort(Student[] s) { for(int i =0; i < s.Length-1; i++)//控制轮数 { for(int j =0; j < s.Length-1- i; j++)//控制交换次数 { if(s[j].id > s[j +1].id) { Student temp = s[j]; s[j]= s[j +1]; s[j +1]= temp; } } } }
然后在相应函数中添加
Sort(s);//完成排序
但是,我们现在又想根据age排序,怎么办呢,当然,我们可以复制粘贴sort()方法,修改比较方法或者使用switch来判断用什么属性排序:
//实现冒泡排序,通过不同的属性排序 private void Sort(Student[] s,string sortfield) { switch (sortfield) { case "id": for (int i = 0; i < s.Length - 1; i++)//控制轮数 { for (int j = 0; j < s.Length - 1 - i; j++)//控制交换次数 { if (s[j].id > s[j + 1].id)//根据id排名 { Student temp = s[j]; s[j] = s[j + 1]; s[j + 1] = temp; } } } break; case "age": for (int i = 0; i < s.Length - 1; i++)//控制轮数 { for (int j = 0; j < s.Length - 1 - i; j++)//控制交换次数 { if (s[j].age > s[j + 1].age)//根据age排名 { Student temp = s[j]; s[j] = s[j + 1]; s[j + 1] = temp; } } } break; } }
该方法的调用为,在响应函数中添加:
Sort(s, "id");//根据id排名 Sort(s, "age");//根据age排名
但是,我们又想用classid排序(不和理),这样我们就需要添加一个case,copy代码,这样做代码重用性不高。现在可以考虑用委托。将比较大小的代码提炼成方法,我们在改变排序的属性时,只需要添加一个新方法即可,不需要修改既有代码。
//使用委托,当然这种可以用泛型来解决 public delegate bool CompareDelegate(Student s1, Student s12);//声明委托 private void Sort(Student[] s,CompareDelegate method)//提供实现方法,在方法中的参数中包含委托实例方法(相当于实例化委托),该方法可以另外添加。 { for (int i = 0; i < s.Length - 1; i++)//控制轮数 { for (int j = 0; j < s.Length - 1 - i; j++)//控制交换次数 { if (method(s[j],s[j+1]))//根据age排名 { Student temp = s[j]; s[j] = s[j + 1]; s[j + 1] = temp; } } } } private bool CompareById(Student s1, Student s2) { return s1.id > s2.id; } private bool CompareByAge(Student s1, Student s2) { return s1.age > s2.age; }
该方法的调用为,在响应函数中添加
//委托实现 Sort(s,CompareById); for (int i = 0; i < s.Length; i++) {
Label2.Text+=s[i].id+","+s[i].name+","+s[i].age+","+s[i].classId+"
"; }
即,调用了根据id排序的方法。
运行结果就不添加了。
四、实现委托的步骤
1、 先声明个委托实例 ;
2、然后提供要处理的方法;
3、再实例化委托(把委托看作是类的话,实例化委托就不难理解了,其参数是要处理的方法,这里的方法 不用加括号,实例化的过程就是装载方法的过程,就好像需要参数的构造函数一样)实例化后的委托对象就好比是c++中的指针,它本身就是封装了方法的对象;
4、最后我们调用委托对象就好比是调用了被封装方法本身,调用时的参数也就传给了被封装的方法。
5、需要注意的是 所声明的委托无论是 参数个数,参数类型,返回值类型 都要和所要封装的方法保持一致,当调用委托实例对象时,所传入的参数也要保持一致 ,否则会出现错误。
五、委托链
我们知道委托是对方法的封装,而且委托可以封装很多方法形成委托链,其实委托就好像是一个容器,他封装了我们想要实现的若干方法,当调用委托对象(相当于c++中的指针)时,就会顺序的执行它所封装的所有的方法,如果有返回值的话,往往返回的是最后一个被执行的方法的返回值,委托链的形成可以用"+="或"-="对不同的委托实例进行二元操作。
六、总结:
委托是一种引用类型,我们在处理他的时候要当作类来看待而不是方法,说白了委托就是对方法或者方法列表的引用,调用一个委托实例就好像是调用c++中的指针一样,他封装了对制定方法的引用,或者说委托起到的是桥梁的作用,实例后的委托对象会将给定的参数传递给他所回调的方法,并去执行方法。