天天看點

委托

注:以下文章僅為學習筆記,很大部分來自于他人部落格或資料,我會相應注明,僅為學習。

一、為什麼要有委托

  要學習委托,我們首先要明白為什麼要有委托,委托的意義在于什麼。

  下面是來自于黑馬論壇上某人的通俗解說,我覺得很好:

    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.csdn.net/huomm/article/details/1897010

  1、 先聲明個委托執行個體  ;

      2、然後提供要處理的方法;

      3、再執行個體化委托(把委托看作是類的話,執行個體化委托就不難了解了,其參數是要處理的方法,這裡的方法 不用加括号,執行個體化的過程就是裝載方法的過程,就好像需要參數的構造函數一樣)執行個體化後的委托對象就好比是c++中的指針,它本身就是封裝了方法的對象;

      4、最後我們調用委托對象就好比是調用了被封裝方法本身,調用時的參數也就傳給了被封裝的方法。

  5、需要注意的是 所聲明的委托無論是 參數個數,參數類型,傳回值類型 都要和所要封裝的方法保持一緻,當調用委托執行個體對象時,所傳入的參數也要保持一緻 ,否則會出現錯誤。 

五、委托鍊

  我們知道委托是對方法的封裝,而且委托可以封裝很多方法形成委托鍊,其實委托就好像是一個容器,他封裝了我們想要實作的若幹方法,當調用委托對象(相當于c++中的指針)時,就會順序的執行它所封裝的所有的方法,如果有傳回值的話,往往傳回的是最後一個被執行的方法的傳回值,委托鍊的形成可以用"+="或"-="對不同的委托執行個體進行二進制操作。

  詳細例子參照:

六、總結:

  委托是一種引用類型,我們在處理他的時候要當作類來看待而不是方法,說白了委托就是對方法或者方法清單的引用,調用一個委托執行個體就好像是調用c++中的指針一樣,他封裝了對制定方法的引用,或者說委托起到的是橋梁的作用,執行個體後的委托對象會将給定的參數傳遞給他所回調的方法,并去執行方法。