4.3 委托(1)
委托技術(shù)是.NET框架中的高級特性之一,也可以說(shuō)是.NET重要技術(shù)之一,是在大多數的技術(shù)筆試、面試中必定出現的部分。委托提供了安全的函數回調(CALL BACK)機制,成為程序員設計靈活簡(jiǎn)潔的.NET程序的重要方法之一。本章節將通過(guò)介紹委托概念和分析一些常見(jiàn)的委托技術(shù)題。
面試題55 什么是委托
.NET面試中經(jīng)常會(huì )被提問(wèn)什么是C#中的委托,面試官主要意圖是考察應聘者對.NET的幾個(gè)重要特性的了解。這樣的細節問(wèn)題往往讓程序員不能夠準確地回答,讀者應該在平時(shí)學(xué)習的過(guò)程中注意積累。
【出現頻率】★★★★★
【關(guān)鍵考點(diǎn)】
委托(Delegate)
委托的特點(diǎn)
【考題分析】
委托是一種引用類(lèi)型,它是函數指針的托管版本。在C#中,委托是一種可以把引用存儲為函數的類(lèi)型。委托可以引用實(shí)例和靜態(tài)方法,而函數指針只能引用靜態(tài)方法。委托的聲明非常類(lèi)似函數,和函數不同的是委托不帶函數體,并且需要使用Delegate關(guān)鍵字。委托的聲明指定了一個(gè)函數簽名,其中包含參數列表和一個(gè)返回類(lèi)型。在定義了委托后,就可以聲明該委托類(lèi)型的變量,然后可以將這個(gè)變量初始化為與該委托有相同簽名的函數進(jìn)行引用,隨后可以使用委托變量調用該函數。
注意:委托雖然與函數指針?lè )浅n?lèi)似,但卻不是指針。許多程序員把.NET中的委托理解為安全的函數指針,這樣的理解是比較牽強的,委托實(shí)現的功能和函數指針?lè )浅n?lèi)似的一點(diǎn)就是提供了程序回調機制。
委托的內部實(shí)現機制和函數指針在指向方法這一點(diǎn)上是完全相同的。委托是安全的,主要因為委托和其他所有的.NET成員一樣,均是一種類(lèi)型,都是System.Delegate的某個(gè)派生類(lèi)的一個(gè)對象。
現在來(lái)分析一個(gè)具體的例子,示例代碼如下:
- using System;
- namespace MyConsole
- {
- class SimpleDelegate
- {
- //定義一個(gè)返回為double類(lèi)型的委托,這個(gè)委托有一個(gè)整型的輸入參數
- public delegate double Delegate_Prod(int a);
- static double fn_Prodvalues(int val1)
- {
- return val1 * val1;
- }
- static void Main(string[] args)
- {
- Delegate_Prod delObj = new Delegate_Prod(fn_Prodvalues);
- //聲明一個(gè)委托
- Console.Write("請輸入數字");
- int v1 = Int32.Parse(Console.ReadLine());
//輸入一個(gè)整型數值- double res = delObj(v1);
//調用委托方法fn_Prodvalues- Console.WriteLine("返回值:" + res); //返回結果
- Console.ReadLine();
- }
- }
- }
示例代碼中,首先通過(guò)public delegate double Delegate_Prod(int a)定義了一種名為Delegate_Prod的新類(lèi)型,這個(gè)類(lèi)型繼承自System.MulticastDelegate。它包含一個(gè)名為Invoke()的方法,該方法接收一個(gè)字符型的參數且沒(méi)有任何返回。這些步驟都是由C#編譯器自動(dòng)完成的。然后,聲明了一個(gè)Delegate_Prod的對象delObj,并且綁定到fn_Prodvalues這個(gè)靜態(tài)方法上。運行結果如下:
- 請輸入數字9
- 返回值: 81
注意:本質(zhì)上,委托的調用就是執行了在定義委托時(shí)所生成的Invoke()方法。
C#中的委托類(lèi)都繼承自System.Delegate類(lèi)型。委托類(lèi)型的聲明與方法簽名相似,有一個(gè)返回值和任意數目任意類(lèi)型的參數。委托是一種可用于封裝命名或匿名方法的引用類(lèi)型。委托類(lèi)似于函數指針,但是委托是類(lèi)型安全和可靠的。
面試題56 C#中被委托的方法必須是靜態(tài)的嗎
關(guān)于委托的方法是否必須是靜態(tài)方法這個(gè)考題,考察點(diǎn)非常明確。面試官一般希望了解應聘者是不是僅僅了解如何定義和簡(jiǎn)單的使用委托,是否真正掌握理解委托的內部原理。
【出現頻率】★★★★★
【關(guān)鍵考點(diǎn)】
靜態(tài)方法與實(shí)例方法的區別
分析委托的方法是否必須是靜態(tài)方法
【考題分析】
在分析這個(gè)問(wèn)題之前,先了解C#幾個(gè)基本的概念。在C#中最常見(jiàn)的方法就是靜態(tài)方法和實(shí)例方法,它們之間的區別如表4.1所示。
表4.1 靜態(tài)方法和實(shí)例方法的區別
靜 態(tài) 方 法 | 實(shí) 例 方 法 |
Static關(guān)鍵字 | 不需要static關(guān)鍵字 |
類(lèi)名調用 | 實(shí)例對象調用 |
可以訪(fǎng)問(wèn)靜態(tài)成員 | 可以直接訪(fǎng)問(wèn)靜態(tài)成員 |
不可以直接訪(fǎng)問(wèn)實(shí)例成員,需要實(shí)例化 | 可以直接訪(fǎng)問(wèn)靜態(tài)成員 |
不能直接調用實(shí)例方法,需要實(shí)例化 | 可以直接調用實(shí)例方法和靜態(tài)方法 |
調用前初始化 | 實(shí)例化時(shí)初始化 |
如表4.1所示,在C#中靜態(tài)方法由關(guān)鍵字static來(lái)定義。靜態(tài)方法不需要實(shí)例化該對象,即可以通過(guò)類(lèi)名來(lái)訪(fǎng)問(wèn)對象。相應地,在靜態(tài)方法中不能直接訪(fǎng)問(wèn)類(lèi)型中任何非靜態(tài)成員。而實(shí)例方法,則需要通過(guò)具體的實(shí)例對象來(lái)調用,并且可以訪(fǎng)問(wèn)實(shí)例對象中的任何成員對象。
委托綁定實(shí)例方法的分析,依據C#實(shí)例方法的定義,當一個(gè)實(shí)例方法被調用時(shí),需要通過(guò)實(shí)例對象來(lái)訪(fǎng)問(wèn),因此綁定實(shí)例方法到委托時(shí),必須同時(shí)讓委托得到該實(shí)例方法的實(shí)例對象的信息。這樣,.NET才能在委托被回調(CallBack)的時(shí)候成功地執行該實(shí)例方法。Delegate.Target屬性是一個(gè)指向目標實(shí)例的引用,當綁定實(shí)例方法給委托時(shí),該參數會(huì )被設置為該方法所在類(lèi)型的一個(gè)實(shí)例對象。
委托綁定靜態(tài)方法的分析。Delegate.Target屬性是一個(gè)指向目標實(shí)例的引用,當綁定一個(gè)靜態(tài)方法給委托時(shí),則該參數會(huì )被設置為null。
注意:如果委托調用一個(gè)或多個(gè)實(shí)例方法,則此屬性返回調用列表中最后一個(gè)實(shí)例方法的目標。
【答案】
通過(guò)上面的分析讀者可以清晰地判別,委托不僅能綁定靜態(tài)方法,同時(shí)也可綁定實(shí)例方法。當綁定實(shí)例方法時(shí),Delegate.Target屬性將會(huì )設置成指向該實(shí)例方法所屬類(lèi)型的一個(gè)實(shí)例對象,當綁定靜態(tài)方法時(shí),Delegate.Target屬性將會(huì )被設置成null。
4.3 委托(2)
面試題57 什么是多播委托
每個(gè)委托都只包含一個(gè)方法調用,調用委托的次數與調用方法的次數相同。如果要調用多個(gè)方法,就需要多次顯式調用這個(gè)委托。當然委托也可以包含多個(gè)方法,這種委托稱(chēng)為多播委托。下面將詳細分析這個(gè)問(wèn)題。
【出現頻率】★★★★
【關(guān)鍵考點(diǎn)】
多播委托的基本概念
System.MulticastDelegate 的特性
【考題分析】
在本章前文中,筆者已經(jīng)詳細地介紹了委托的基本概念。而所謂的多播委托,是指引用多個(gè)方法的委托,當調用委托時(shí),它連續調用每個(gè)方法。在調用過(guò)程中,委托必須為同類(lèi)型,返回類(lèi)型也必須是void,且不能帶輸出參數(但可以帶引用參數),這樣才能將委托的單個(gè)實(shí)例合并為一個(gè)多播委托。除此之外,多番委托的聲明和實(shí)例化方法都和其他委托沒(méi)有什么不同?,F在來(lái)看一個(gè)實(shí)際的例子,示例代碼如下:
- using System;
- namespace MyConsole
- {
- class MulticastDelegate
- {
- //定義委托,接收一個(gè)字符型參數
- public delegate void hellokittyHander(string msg);
- private void hellokitty1(string msg)
- //定義一個(gè)簡(jiǎn)單
的私有方法hellokitty1()- {
- Console.WriteLine("hello kitty1" + msg);
- }
- private void hellokitty2(string msg)
- //定義一個(gè)簡(jiǎn)單
的私有方法hellokitty2()- {
- Console.WriteLine("hello kitty2" + msg);
- }
- private void hellokitty3(string msg)
- //定義一個(gè)簡(jiǎn)單的
私有方法hellokitty3()- {
- Console.WriteLine("hello kitty3" + msg);
- }
- static void Main(string[] args)
- {
- MulticastDelegate SimpleDelegate = new
MulticastDelegate();- //聲明一個(gè)委托變量,并綁定第一個(gè)方法
- hellokittyHander hellokitty = new
hellokittyHander(SimpleDele-- gate.hellokitty1);
- hellokitty += new hellokittyHander(SimpleDelegate.hellokitty2);
- //綁定第二個(gè)方法
- hellokitty += new hellokittyHander(SimpleDelegate.hellokitty3);
- //綁定第三個(gè)方法
- //刪除已經(jīng)調用委托方法
- hellokitty -= new hellokittyHander
(SimpleDelegate.hellokitty2);- if (hellokitty != null)
- hellokitty("這是一個(gè)委托demo");
- Console.ReadLine();
- }
- }
- }
簡(jiǎn)單分析這個(gè)例子,示例代碼中首先定義了一個(gè)帶string參數無(wú)返回的委托hellokittyHander,在Main()函數中聲明了一個(gè)hellokittyHander的委托變量hellokitty,并綁定了第一個(gè)方法void hellokitty1(string msg)。然后,通過(guò)hellokitty += new new hellokittyHander(SimpleDelegate.hellokitty1)初始化了第二個(gè)委托,并且綁定了void hellokitty2(string msg) ,同時(shí)把第二個(gè)委托掛在第一個(gè)委托之后。緊接著(zhù),掛上第三個(gè)委托并綁定到 void hellokitty3(string msg)。這是一種比較簡(jiǎn)單明了的寫(xiě)法,在開(kāi)發(fā)ASP.NET 或者Windows應用程序時(shí),當一個(gè)按鈕事件被添加時(shí),Visual Studio就會(huì )為程序生成類(lèi)似的代碼。
代碼運行結果如下:
- hello kitty1 這是一個(gè)委托demo
- hello kitty3 這是一個(gè)委托demo
在這里讀者可能會(huì )對這個(gè)輸出的代碼產(chǎn)生困惑,為什么hello kitty2沒(méi)有被輸出。事實(shí)上細心的讀者會(huì )發(fā)現在上面的程序中有一句取消綁定的事件,代碼如下:
- //刪除已經(jīng)調用委托方法
- hellokitty -= new hellokittyHander
(SimpleDelegate.hellokitty2);
注意:多播委托是指一個(gè)委托的鏈表,而不是指另一類(lèi)特殊的委托。當執行鏈上的一個(gè)方法,后續委托方法將會(huì )依次被執行。System.MulticastDelegate定義了對多播委托的支持。
【答案】
多播委托是指一個(gè)由委托串成的鏈表,當鏈表上的一個(gè)委托被回調時(shí),所有鏈表上該委托的后續委托將會(huì )被順序執行。但要注意,多播委托必須是同類(lèi)型的,返回類(lèi)型必須是void,并且不能帶輸出參數(但可以帶引用參數)。
面試題58 列舉一個(gè)C#中的委托應用
列舉C#中的委托應用是一個(gè)比較抽象的問(wèn)題,面試官旨在考察應聘者對委托的使用經(jīng)驗和設計能力。為了解釋這一問(wèn)題,本小節將帶領(lǐng)讀者實(shí)際體會(huì )一個(gè)具體的例子。為了能夠熟悉地運用委托,讀者仍然需要在實(shí)際工作學(xué)習中不斷地積累經(jīng)驗。
【出現頻率】★★★★
【關(guān)鍵考點(diǎn)】
委托的設計
委托的應用
【考題分析】
排序系統在大多數系統中都有所應用,在本小節中,筆者將舉一個(gè)學(xué)生成績(jì)排序的例子來(lái)說(shuō)明委托的實(shí)際應用。下列代碼展示了這個(gè)學(xué)生成績(jì)排序系統的簡(jiǎn)單實(shí)現方式。
示例中定義了學(xué)生類(lèi)型的基本成員、構造方法,在私有構造方法中,為學(xué)生排序委托定義了默認的邏輯,示例代碼如下:
4.3 委托(3)
定義成績(jì)排序的委托方法,并將這個(gè)方法添加到委托對象之中。以下示例代碼使用冒泡法排序法對數組進(jìn)行排序。
- ///<summary>
- ///排序類(lèi),方法Sort為靜態(tài)方法,使用冒泡法
- ///</summary>
- class ScoreSort
- {
- ///<summary>
- ///冒泡法排序
- ///</summary>
- ///<param name="sortArray">要進(jìn)行排序的數組</param>
- ///<param name="Method">排序的方法</param>
- static public void Sort(object[] sortArray, CompareOp Method)
- {
- //外圍第一次循環(huán)
- for (int i = 0; i < sortArray.Length; i++)
- {
- //第二次循環(huán)
- for (int j = i + 1; j < sortArray.Length; j++)
- {
- //比較大小并交換
- if (Method(sortArray[j], sortArray[i]))
- {
- object temp = sortArray[i];
- sortArray[i] = sortArray[j];
- sortArray[j] = temp;
- }
- }
- }
- }
- }
最后可以編寫(xiě)測試代碼,來(lái)測試學(xué)生排序的功能,示例代碼如下:
- using System;
- using System.Collections.Generic;
- using System.Text;
- namespace MyConsole
- {
- class StudentSort
- {
- //定義一個(gè)委托
- delegate bool CompareOp(object lhs, object rhs);
- static void Main(string[] args)
- {
- //初始化一個(gè)學(xué)生數組
- Student[] students =
- {
- new Student("Zhang", 86),
- new Student("Li", 75),
- new Student("Wang", 93),
- new Student("Zhao", 68),
- new Student("Liu", 53),
- new Student("Jia", 100),
- new Student("Pan", 83)
- };
- //使用RhsIsGreater方法進(jìn)行比較,并用Sort方法排序
- CompareOp studentCompareOp = new CompareOp(Student.RhsIsGrea-
- ter);
- ScoreSort.Sort(students, studentCompareOp);
- //循環(huán)輸出所有學(xué)生的已排序成績(jì)
- for (int i = 0; i < students.Length; i++)
- {
- Console.WriteLine(students[i].ToString());
- }
- Console.ReadLine();
- }
- }
- }
下面是示例代碼執行后的輸出:
- Liu: 53
- Zhao: 68
- Li: 75
- Pan: 83
- Zhang: 86
- Wang: 93
- Jia: 100
【答案】
委托的應用環(huán)境通常是任務(wù)的執行者把細節任務(wù)進(jìn)行再次分配,執行者明確地知道被執行的工作是什么,而且可以把這些執行細節委托給其他組件、方法或者程序集。
聯(lián)系客服