委托

注:以下文章仅为学习笔记,很大部分来自于他人博客或资料,我会相应注明,仅为学习。

 

一、为什么要有委托

  要学习委托,我们首先要明白为什么要有委托,委托的意义在于什么。

  下面是来自于黑马论坛上某人的通俗解说,我觉得很好:

    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,双击产生相应代码

<asp:Button ID="Button2" runat="server" Text="委托排序" onclick="Button2_Click" />
<br />
<asp:Label ID="Label2" runat="server" Text=""></asp:Label>

在相应代码中new一些Student的数组成员出来

protected void Button2_Click(object sender, EventArgs e)
{
            //声明一个Student的数组
            //为什么不用List或者List<T>呢,因为二者都是微软封装好的数据结构,实现了排序,没必要我画蛇添足
            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+"<br/>"; }

即,调用了根据id排序的方法。

运行结果就不添加了。

四、实现委托的步骤

  来源参照:http://blog.****.net/huomm/article/details/1897010

  1、 先声明个委托实例  ;

      2、然后提供要处理的方法;

      3、再实例化委托(把委托看作是类的话,实例化委托就不难理解了,其参数是要处理的方法,这里的方法 不用加括号,实例化的过程就是装载方法的过程,就好像需要参数的构造函数一样)实例化后的委托对象就好比是c++中的指针,它本身就是封装了方法的对象;

      4、最后我们调用委托对象就好比是调用了被封装方法本身,调用时的参数也就传给了被封装的方法。

  5、需要注意的是 所声明的委托无论是 参数个数,参数类型,返回值类型 都要和所要封装的方法保持一致,当调用委托实例对象时,所传入的参数也要保持一致 ,否则会出现错误。 

五、委托链

  我们知道委托是对方法的封装,而且委托可以封装很多方法形成委托链,其实委托就好像是一个容器,他封装了我们想要实现的若干方法,当调用委托对象(相当于c++中的指针)时,就会顺序的执行它所封装的所有的方法,如果有返回值的话,往往返回的是最后一个被执行的方法的返回值,委托链的形成可以用"+="或"-="对不同的委托实例进行二元操作。

  详细例子参照:http://blog.****.net/huomm/article/details/1897010

六、总结:

  委托是一种引用类型,我们在处理他的时候要当作类来看待而不是方法,说白了委托就是对方法或者方法列表的引用,调用一个委托实例就好像是调用c++中的指针一样,他封装了对制定方法的引用,或者说委托起到的是桥梁的作用,实例后的委托对象会将给定的参数传递给他所回调的方法,并去执行方法。

上一篇:将时间字符串格式化成DateTime的有趣事情


下一篇:在 ASP.NET 中执行 URL 重写的方法