程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#基礎知識 >> 學習和理解C#的委托

學習和理解C#的委托

編輯:C#基礎知識

  去年自學C#用的教程是入門級的《學通C#的24堂課》,教材裡面也沒有提到委托和事件,工作中也沒怎麼用到。後來一次在網上看了一些大牛的博客,讀完之後感覺懵懵懂懂,似懂非懂,過了兩三天之後,卻又全然忘記了。畢竟學習這事,溫故而知新,學了不用,自然忘得也很快。對於如我一樣的初學者來說,較好地理解委托和事件並是一件容易的事。其實掌握了的人,會覺得也沒什麼,而沒有掌握的人,每次見到委托和事件就會覺得很畏懼。前段時間看到張旭亮老師的博客中關於.NET 開發系列PPT中提到一個觀點,沒學會委托就等不會.NET。我深受激勵,所以這次下定決心認認真真學習了一下,並將自己的一些理解記錄下來。

   委托,字面上的意思就是請別人幫忙做一些事情。讀完文章之後你會發現,委托是指向一個方法的指針,通過指定一個委托名稱,即可通過委托來調用方法。實際上與字面意思差不多。

  什麼情況下適合使用委托

  首先通過一個例子來說明什麼情況下使用委托。

  以學生值日為例,值日生要做開門、擦黑板的工作。對應的方法形式如下:

public void OpenDoor(){
    //開門具體實現
    ...
}

public void CleanBlackBoard(){
    //擦黑板具體實現
    ...
}

  下面再寫一個值日的方法,要完成上述這些工作:

public void OnDuty(){
    OpenDoor();
    CleanBlackBoard();
}

  這樣,值日生開門、擦黑板的工作就實現了。但是這種方法的擴展性和靈活性不是很好,假設現在值日生工作又增加了,需要關燈,方法如下:

public void TurnOffLight(){
    //關燈的具體邏輯
    ...
}

  我們就需要修改OnDuty方法:

public void OnDuty(){
    OpenDoor();
    CleanBlackBoard();
    TurnOffLight();
}

  上面的代碼沒有使用委托,但是還是實現了需要完成的工作。很容易發現,開門、擦黑板、關燈,雖然他們的方法名稱不同,但是它們具有相同的“形式”——它們都不獲取參數,也都沒有返回值(void),這正是委托可以發揮作用的時候。使用於這種形式匹配的一個委托。就可以引用任何工作的方法。我們可以聲明如下的一個委托:

public delegate void DoSomeWorkDelegate();

 注意:

  • 聲明委托時,要使用 delegate 關鍵字。
  • 委托定義了它能引用的方法的“形式”。你要指定返回類型(本例是void)、委托名稱(DoSomeWorkDelegate)以及任何參數(本例無參數)。

  定義好委托之後,就可以創建它的一個實例,並使用 “+=”操作符,讓這個實例引用一個相匹配的方法。代碼如下:

public delegate void DoSomeWorkDelegate();
public DoSomeWorkDelegate doSomeWork;

doSomeWork+=OpenDoor;

  上例中的方法很簡單,既沒有返回值,又沒有參數。目的只是了解在何種情況下使用委托。下面通過一個帶參數的例子來說明繼續說明如何使用委托。

   如何使用委托

  此處以聽課為例,為了對比,同樣先不使用委托,先上代碼:

public void AttendClass(string name) {
    // 做某些額外的事情,比如初始化之類,此處略
    ChineseClass(name);
}
public void ChineseClass(string name) {
     //具體聽語文課的邏輯 
     ...  
}

  上面這段代碼表示。AttendClass 表示聽課,當我們傳遞代表學生姓名 name 參數,比如說“Jhon”,進去的時候,在這個方法中,將調用ChineseClass 方法,再次傳遞 Jhon 參數, 表示Jhon 參加了語文課。

  假設現在開設了數學課,我們需要添加新的方法:

public void MathClass(string name){
    //具體聽數學課的邏輯
...   }

  這時候 AttendClass 也要修改,在此之前先定義一個枚舉用作判斷:

public enum Subject{
    Chinese,Math
}

public void AttendClass(string name, Subject sub){
    //做某些額外的事情,比如初始化之類,此處略
    swith(sub){
        case Subject.Chinese:
ChineseClass(name); break; case Subject.Math: MathClass(name); break; } }

  OK,現在問題解決了,誠如前一個例子所說,假如現在又要增加英語課,體育課,就不得不反復修改枚舉和 AttendClass 。可見程序的可拓展性很不好。

如前例,對於上課的方法,不論是什麼課,它們都具有相同的“形式”,這樣我們即可使用委托。

      在考慮如何使用委托之前,我們先看看 AttendClass  的方法簽名:

public void AttendClass(string name, Subject sub)

  我們僅看 string name,在這裡,string 是參數類型,name 是參數變量,當我們給字符串那麼賦值時,我們可以在方法體內對這個name進行其他操作.現在假如現在讓 AttendClass() 方法接受一個參數變量,這個變量可以代表另一個方法,當給這個變量賦值為 ChineseClass 時,它代表 ChineseClass() 這個方法,當給它賦值為 MathClass 時,他代表MathClass() 這個方法。就是說讓用一個參數來代方法。比如,把這個參數命名為,SubClass ,然後我們在方法體內,就像使用參數一樣使用SubClass。由於 SubClass 代表著一個方法,它的使用方式應該和它被賦值的方法(比如 ChineseClass)是一樣的,比如

SubClass(string name)

  按照這樣的思路這樣,AttendClass(),就應該是這個樣子的:

public void AttendClass(string name,*** SubClass){
    SubClass(name);
}

  注意到 *** ,這個位置通常放置的應該是參數的類型,現在就出現了一個問題:這個代表著方法的SubClass參數應該是什麼類型的?

  如果我說答案就是委托。它定義了 SubClass 方法的種類,也就是 SubClass 方法參數的類型。如同 string 決定了AttendClass() 這個方法的第一個參數的類型一樣,委托決定 AttendClass 方法中的第二個參數的類型。

  本例中委托的定義如下:

public delegate void ClassDelegate(string name);

  現在再次改動 AttendClass 方法:

public void AttendClass(string name,ClassDelegate SubClass){
    SubClass(name);
}

  委托ClassDelegate出現的位置與 string 相同,string是一個類型,那麼ClassDelegate 應該也是一個類型,或者叫類(Class)。但是委托的聲明方式和類卻完全不同。那麼本例完整有關上課的類的代碼如下:

public delegate void ClassDelegate(string name);
public class OnClass{
        //其它代碼
        ...
        public void AttendClass(string name,ClassDelegate SubClass){
                SubClass(name);
        }
        
        public void ChineseClass(string name){
                ...
        }
        public void MathClass(string name){
                ...
        }
}

  由此可見,委托是一種類型。它定義了方法的類型,實際上就是把方法當做變量。(注意與指針的區別)

  那麼我們如何使用它呢,代碼如下:

private OnClass objOnClass;
objOnClass=new OnClass();
string name1="xiaoming";
string name2="小張";
objOnClass.AttendClass(name1,ChineseClass);
objOnClass.AttendClass(name2,MathClass);

  上面的代碼表示 xiaoming 參加了語文課,小張參加了數學課。既然委托是一種類型。那麼我們也可以以下面的方式來運用委托

ClassDelegate delegate1,delegate2;
delegate1=objOnClass.ChineseClass;
delegate2=objOnClass.MathClass;
string name1="xiaoming";
string name2="小張";
objOnClass.AttendClass(name1,delegate1);
objOnClass.AttendClass(name2,delegate2);

  還可以將多個方法付給同一個委托,下面的代碼表示小張既參加了語文課,又參加了數學課

ClassDelegate delegate3;
delegate3=objOnClass.ChineseClass;
delegate3+=objOnClass.MathClass;
string name1="小張";
objOnCLass.AttendClass(name1,delegate3);

  可以看到,為委托綁定方法我們使用 重載了的 += 這個符號。如果將上面的代碼中第一次綁定方法 delegate3=ChineseClass 改為 delegate3+=ChineseClass,將會出現“使用了未賦值的局部變量”的錯誤。我們可以像下面這樣使用委托,給它初始化賦值

ClassDelegate delegate3=new ClassDelegate(objOnClass.ChineseClass);
string name1="xiaoming";
objOnClass.AttendClass(name1,delegate3);

  我們甚至可以不用AttendClass這個方法,像下面這樣來使用委托

ClassDelegate delegate3=new ClassDelegate(objOnClass.ChineseClass);
string name1="xiaoming";
delegate3(name1);

  所以,可以將多個方法綁定到一個委托上面,調用的時候依次執行。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved