委托概述
成都創(chuàng)新互聯(lián)企業(yè)建站,10余年網(wǎng)站建設(shè)經(jīng)驗(yàn),專注于網(wǎng)站建設(shè)技術(shù),精于網(wǎng)頁(yè)設(shè)計(jì),有多年建站和網(wǎng)站代運(yùn)營(yíng)經(jīng)驗(yàn),設(shè)計(jì)師為客戶打造網(wǎng)絡(luò)企業(yè)風(fēng)格,提供周到的建站售前咨詢和貼心的售后服務(wù)。對(duì)于成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)中不同領(lǐng)域進(jìn)行深入了解和探索,創(chuàng)新互聯(lián)在網(wǎng)站建設(shè)中充分了解客戶行業(yè)的需求,以靈動(dòng)的思維在網(wǎng)頁(yè)中充分展現(xiàn),通過對(duì)客戶行業(yè)精準(zhǔn)市場(chǎng)調(diào)研,為客戶提供的解決方案。如何聲明委托?
如何使用委托?
Action
多播委托是什么?
匿名方法
委托是尋址方法的.Net版本,是類型安全的類,它定義了返回類型和參數(shù)類型。委托類不僅包含對(duì)單個(gè)方法的引用,也可以包含對(duì)多個(gè)方法的引用。
委托是一個(gè)特殊類型的對(duì)象,其特殊之處在于,我們之前定義的所有對(duì)象包含數(shù)據(jù),而委托包含的只是一個(gè)或多個(gè)方法的地址。
委托和lambda表達(dá)式直接相關(guān),當(dāng)參數(shù)是委托的時(shí)候,我們可以使用lamabda表達(dá)式實(shí)現(xiàn)委托引用的方法。
我們都知道在C#中使用一個(gè)類的時(shí)候分為兩個(gè)階段。首先,需要定義這個(gè)類(告訴編譯器這個(gè)類由什么字段和方法組成)。然后實(shí)例化類的一個(gè)對(duì)象(靜態(tài)方法除外)。使用委托時(shí),我們也需要經(jīng)過類似以上的兩個(gè)步驟。首先必須定義要使用的委托,對(duì)于委托,定義它就是告訴編譯器這種類型的委托表示哪種類型的方法。然后,必須創(chuàng)建該委托的一個(gè)或多個(gè)實(shí)例。編譯器在后臺(tái)將創(chuàng)建表示該委托的類。
委托的基本語(yǔ)法如下:
delegate void IntMethodInvoker(int x);
注釋:上述基本語(yǔ)法案例是說定義了一個(gè)委托IntMethodInvoker,并且指定了該委托的每個(gè)實(shí)例都可以包含一個(gè)方法(該方法帶有一個(gè)int參數(shù))的引用,并且方法返回值是void(空)。理解委托的一個(gè)要點(diǎn)是他們的類型安全性非常高。在定義委托時(shí),必須給出它所表示的方法的簽名和返回類型等全部細(xì)節(jié)。說白了就是給方法的簽名和返回類型指定名稱。
假如我們有如下需求:有一個(gè)方法需要使用委托,該方法的接收兩個(gè)long型參數(shù),并且返回值是一個(gè)double類型,那我我們?yōu)樵摲椒ń⒌奈腥缦拢?/p>
delegate double TwoLongsOp(long first, long second);
注釋:其語(yǔ)法類似于方法的定義,但是沒有方法體。定義的前面需要加上關(guān)鍵字delegate。因?yàn)槎x委托基本上是定義一個(gè)新類,所以可以在定義類的任何相同地方定義委托。也就是說,可以在另一個(gè)類的內(nèi)部定義,也可以在任何類的外部定義,還可以在名稱空間中把委托定義為頂層對(duì)象。根據(jù)定義的可見性和委托的作用域,可以在委托的定義上應(yīng)用任意常用的訪問修飾符:Public、private、protected等。
實(shí)際上,“定義一個(gè)委托”是指“定義一個(gè)新類”。委托實(shí)現(xiàn)為派生自基類System.MulticastDelegate的類,System.MulticastDelegate又派生自基類System.Delegate.C#編譯器能識(shí)別這個(gè)類,會(huì)使用其委托語(yǔ)法,因此我們不需要了解這個(gè)類的具體執(zhí)行情況。這是C#與基類共同合作,使編程更易完成的另一個(gè)范例。
定義好委托后,就可以創(chuàng)建它的一個(gè)實(shí)例,從而用它存儲(chǔ)特定方法的細(xì)節(jié)。
但是,在術(shù)語(yǔ)方面有一個(gè)問題,類有兩個(gè)不同的術(shù)語(yǔ):“類”表示較廣義的定義,“對(duì)象”表示類的實(shí)例。但委托只有一個(gè)術(shù)語(yǔ)。在創(chuàng)建委托的實(shí)例,所創(chuàng)建的委托的實(shí)例仍稱為委托,必須從上下文中確定委托的確切含義。
下面這段代碼說明如何使用委托(簡(jiǎn)單應(yīng)用):
using System; namespace Wrox.ProCSharp.Delegates { class Program { private delegate string GetAString(); static void Main() { int x = 40; GetAString firstStringMethod = new GetAString(x.ToString); Console.WriteLine("String is {0}", firstStringMethod.Invoke()); Console.ReadLine(); } } }
在這段代碼中,實(shí)例化了類型為GetString的一個(gè)委托,并對(duì)它進(jìn)行初始化,使它引用整型變量x的ToString()方法。在C#中,委托在語(yǔ)法上總是接收一個(gè)參數(shù)的構(gòu)造函數(shù),這個(gè)參數(shù)就是委托引用的方法。這個(gè)方法必須匹配最初定義委托時(shí)的簽名。所以在這個(gè)示例中,如果不帶x.ToString來初始化變量firstStringMethod變量會(huì)報(bào)編譯錯(cuò)誤。注意:因?yàn)閕nt.ToString()是一個(gè)實(shí)例方法(不是靜態(tài)方法),所以需要指定實(shí)例(x)和方法名來正確地初始化委托。
在上述代碼中的firstStringMethod.Invoke()和firstStringMethod()方法是會(huì)產(chǎn)生完全相同的效果的。Invoke()是委托類的方法。因?yàn)閒irstStringMethod是委托類型的一個(gè)變量,所以C#編譯器會(huì)用firstStringMethod.Invoke()代替firstStringMethod()。
為了減少輸入量,只要需要委托實(shí)例,就可以只傳送地址名稱。這就稱為委托推斷。只要編譯器可以把委托實(shí)例解析為特定的類型,這個(gè)C#特性就是有效的,所以上面的代碼還有另外一種寫法:
using System; namespace Wrox.ProCSharp.Delegates { class Program { private delegate string GetAString(); static void Main() { int x = 40; GetAString firstStringMethod = x.ToString; Console.WriteLine("String is {0}", firstStringMethod.Invoke()); Console.ReadLine(); } } }
注意:調(diào)用上述方法名時(shí)輸入形式不能為x.ToString()(不要輸入圓括號(hào)),也不能把它傳送給委托變量。輸入圓括號(hào)調(diào)用一個(gè)方法。調(diào)用x.ToString()方法會(huì)返回一個(gè)不能賦予委托變量的字符串對(duì)象。只能把方法的地址賦予委托變量。
委托的一個(gè)特征是它們的類型是安全的,可以確保被調(diào)用的方法簽名是正確的。但是它不關(guān)心在什么類型的對(duì)象上調(diào)用該方法,甚至不考慮該方法是靜態(tài)方法,還是實(shí)例方法。
首先建立一個(gè)動(dòng)靜態(tài)都包含的Currency.cs類:
namespace Wrox.ProCSharp.Delegates { struct Currency { public uint Dollars; public ushort Cents; public Currency(uint dollars, ushort cents) { this.Dollars = dollars; this.Cents = cents; } public override string ToString() { return string.Format("${0}.{1,-2:00}", Dollars, Cents); } public static string GetCurrencyUnit() { return "Dollar"; } } }
然后使用使用GetAString實(shí)例:
using System; namespace Wrox.ProCSharp.Delegates { class Program { private delegate string GetAString(); static void Main() { Currency balance = new Currency(34, 50); //firstStringMethod reference an instance method GetAString firstStringMethod = balance.ToString; Console.WriteLine("String is{0}", firstStringMethod.Invoke()); //firstStringMethod reference a static method firstStringMethod = Currency.GetCurrencyUnit; Console.WriteLine("String is {0}", firstStringMethod.Invoke()); Console.ReadLine(); } } }
注釋:上面說明了如何通過委托調(diào)用動(dòng)靜態(tài)方法,只要每個(gè)方法的簽名匹配委托定義即可。
首先新建一個(gè)包含正方形和長(zhǎng)方形的計(jì)算方法的類MathOperation.cs,該類包含動(dòng)靜態(tài)兩種方法:
namespace Wrox.ProCSharp.Delegates { class MathOperations { public static double MultiplyByTwo(double value) { return value * 2; } public static double Square(double value) { return value * value; } } }
然后建立委托來調(diào)用這些方法
using System; namespace Wrox.ProCSharp.Delegates { delegate double DoubleOp(double x); class Program { static void Main() { DoubleOp[] operations ={ MathOperations.MultiplyByTwo, MathOperations.Square }; for (int i = 0; i < operations.Length; i++) { Console.WriteLine("Using operation[{0}]", i); ProcessAndDisplayNumber(operations[i], 2.0); ProcessAndDisplayNumber(operations[i], 7.94); ProcessAndDisplayNumber(operations[i], 1.414); Console.WriteLine(); } Console.ReadLine(); } static void ProcessAndDisplayNumber(DoubleOp action, double value) { double result = action(value); Console.WriteLine("Value is {0},result of operation is {1}", value, result); } } }
運(yùn)行結(jié)果:
Using operation[0] Value is 2,result of operation is 4 Value is 7.94,result of operation is 15.88 Value is 1.414,result of operation is 2.828 Using operation[1] Value is 2,result of operation is 4 Value is 7.94,result of operation is 63.0436 Value is 1.414,result of operation is 1.999396
注釋:在這段代碼中,實(shí)例化了一個(gè)DoubleOp委托數(shù)組(需要我們了解的是,一旦定義了委托類,基本上就可以實(shí)例化它的實(shí)例,就像處理一般的類那樣--所以把一些委托的實(shí)例放在數(shù)組中是可以的)。該數(shù)組的每個(gè)元素都初始化為由MathOperation類實(shí)現(xiàn)的不同操作。然后遍歷這個(gè)數(shù)組,每個(gè)操作應(yīng)用到三個(gè)不同的值上。這就就說了使用委托的一種方式--把方法組合到一個(gè)數(shù)組中來使用,這樣就可以在循環(huán)中調(diào)用不同的方法了。
簡(jiǎn)單案例的代碼解析:
這段代碼的最為關(guān)鍵的一行是把每個(gè)委托傳遞給ProcessAndDisplayNumber方法,比如:
ProcessAndDisplayNumber(operations[i], 2.0);
其中傳遞了委托名,但不帶任何參數(shù)。其語(yǔ)法是:
1、operations[i]表示“這個(gè)委托”,也可以說成是委托表示的方法。
2、2.0表示的是一個(gè)參數(shù)。
3、然后調(diào)用 double result = action(value);這個(gè)實(shí)際上是調(diào)用委托實(shí)例封裝的方法。其返回結(jié)果存儲(chǔ)在result中。
除了為每個(gè)參數(shù)和返回類型定義一個(gè)新委托類型之外,還可以使用Action
泛型Action
Func
因此利用Func
using System; namespace Wrox.ProCSharp.Delegates { class Program { static void Main() { Func[] operations ={ MathOperations.MultiplyByTwo, MathOperations.Square }; for (int i = 0; i < operations.Length; i++) { Console.WriteLine("Using operation[{0}]", i); ProcessAndDisplayNumber(operations[i], 2.0); ProcessAndDisplayNumber(operations[i], 7.94); ProcessAndDisplayNumber(operations[i], 1.414); Console.WriteLine(); } Console.ReadLine(); } static void ProcessAndDisplayNumber(Func action, double value) { double result = action(value); Console.WriteLine("Value is {0},result of operation is {1}", value, result); } } }
如果要調(diào)用多個(gè)方法,就需要多次顯示調(diào)用這個(gè)委托。但是,委托也可以包含多個(gè)方法。這種包含多個(gè)方法的委托就被稱為多播委托。如果調(diào)用多播委托,就可以按順序連續(xù)調(diào)用多個(gè)方法。為此,委托簽名就必須返回void。否則,就只能得到委托調(diào)用的最后一個(gè)方法結(jié)果。
我們明顯可以利用的是返回類型為void的Action
首先建立一MathOperations.cs類:
using System; namespace Wrox.ProCSharp.Delegates { class MathOperations { public static void MultiplyByTwo(double value) { double result = value * 2; Console.WriteLine("Multiplying by 2: {0} gives {1}", value, result); } public static void Square(double value) { double result = value * value; Console.WriteLine("Squaring: {0} gives {1}", value, result); } } }
然后建立調(diào)用當(dāng)前方法的委托函數(shù):
using System; namespace Wrox.ProCSharp.Delegates { class Program { static void Main() { Actionoperations = MathOperations.MultiplyByTwo; operations += MathOperations.Square; ProcessAndDispalyNumber(operations, 2.0); ProcessAndDispalyNumber(operations, 7.94); ProcessAndDispalyNumber(operations, 1.414); Console.WriteLine(); Console.ReadLine(); } static void ProcessAndDispalyNumber(Action action, double value) { Console.WriteLine(); Console.WriteLine("ProcessAndDispalyNumber called with value ={0}", value); action(value); } } }
注釋:多播委托可以識(shí)別運(yùn)算符“+”和“+=”、“-”和“-=”。多播委托將兩個(gè)執(zhí)行方法通過“+”和“+=”連在一起(叫做擴(kuò)展),還可以利用“-”和“-=”刪除方法調(diào)用。多播實(shí)際上是一個(gè)派生自System.MulticastDelegate的類,System.MuticastDelegate又派生自基類System.Delegate。System.MulticastDelegate的其他成員允許把多個(gè)方法調(diào)用鏈接一個(gè)列表。
注意:如果使用多播委托,我們需要了解對(duì)同一個(gè)委托調(diào)用方法鏈的順序并未正式定義。因此應(yīng)避免依賴于以特定順序調(diào)用方法代碼。還有個(gè)問題是多播委托包含一個(gè)逐個(gè)調(diào)用的委托集合。如果通過委托調(diào)用其中的一個(gè)方法拋出異常,則整個(gè)迭代都會(huì)停止。
下面將展示如果多播委托第一個(gè)拋出錯(cuò)誤第二個(gè)方法無法正常執(zhí)行的案例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Wrox.ProCSharp.Delegates { class Program { static void One() { Console.WriteLine("One"); throw new Exception("Error in one"); } static void Two() { Console.WriteLine("Two"); } static void Main() { Action d1 = One; d1 += Two; try { d1(); } catch (Exception) { Console.WriteLine("Exception caught"); } } } }
注釋:one()方法我們故意加了個(gè)異常問題,那么執(zhí)行Main()的時(shí)候會(huì)自動(dòng)的在One()方法執(zhí)行的時(shí)候進(jìn)入catch
我們?nèi)绻鉀Q上面的這種多播委托存在的問題的,我們可以使用Delegate定義GetInvocationList()方法,返回一個(gè)Delegate對(duì)象數(shù)組。然后循環(huán)迭代方法列表,捕獲異常進(jìn)行下次迭代。下面代碼是重寫后的Main()函數(shù):
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Wrox.ProCSharp.Delegates { class Program { static void One() { Console.WriteLine("One"); throw new Exception("Error in one"); } static void Two() { Console.WriteLine("Two"); } static void Main() { Action d1 = One; d1 += Two; Delegate[] delegates = d1.GetInvocationList(); foreach (Action d in delegates) { try { d.Invoke(); } catch (Exception) { Console.WriteLine("Exception caught"); } } } } }
這是C# 2.0之前的表示方式,在C#3.0里面可以使用lambda表達(dá)式代替匿名函數(shù)。我們熟悉的是要想使用委托工作,方法必須是已經(jīng)存在的。但是還有另外一種使用委托的方式:匿名方法
實(shí)際上匿名方法與實(shí)例委托的區(qū)別只是在實(shí)例化委托的時(shí)候,那么我們首先先看下面的代碼是如何使用匿名方法的:
using System; namespace Wrox.ProCSharp.Delegates { class Program { static void Main() { string mid = ", middle part,"; FuncanonDel = delegate(string param) { param += mid; param += " and this was added to the string."; return param; }; Console.WriteLine(anonDel("Start of string")); } } }
注釋:Func
在使用匿名方法的時(shí)候,必須遵循的原則有兩條:一是不能使用跳轉(zhuǎn)語(yǔ)句(break、go或continue)跳轉(zhuǎn)到該匿名方法的外部,反之亦然:匿名方法外部的跳轉(zhuǎn)語(yǔ)句不能跳轉(zhuǎn)到該匿名方法的內(nèi)部。二是在匿名方法的內(nèi)部不能訪問不安全的代碼,另外也不能訪問匿名方法外部使用的ref和out參數(shù)。但可以使用在匿名方法外部定義的其他變量。
注意:如果需要用匿名方法多次編寫同一個(gè)功能,就不要使用匿名方法了。此時(shí)與復(fù)制代碼相比,編寫一個(gè)命名方法比較好,因?yàn)樵摲椒ㄖ恍枰帉懸淮?,以后可以通過名稱引用它。
為了說明委托的實(shí)際用途。我們編寫一個(gè)類BubbleSorter,它實(shí)現(xiàn)一個(gè)靜態(tài)方法Sort(),這個(gè)方法的第一參數(shù)是一個(gè)對(duì)象數(shù)組,把該數(shù)組按照升序重新排列。
需求是本公司員工的工資需要按照需要的順序進(jìn)行排序:
首先定義一個(gè)BubbleSorter類:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Wrox.ProCSharp.Delegates { class BubbleSorter { static public void Sort(IList sortArray, Func comparison) { bool swapped = true; do { swapped = false; for (int i = 0; i < sortArray.Count - 1; i++) { if (comparison(sortArray[i + 1], sortArray[i])) { T temp = sortArray[i]; sortArray[i] = sortArray[i + 1]; sortArray[i + 1] = temp; swapped = true; } } } while (swapped); } } }
定義員工類,該員工類Employee.cs如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Wrox.ProCSharp.Delegates { class Employee { public Employee(string name, decimal salary) { this.Name = name; this.Salary = salary; } public string Name { get; private set; } public decimal Salary { get; private set; } public override string ToString() { return string.Format("{0}, {1:C}", Name, Salary); } public static bool CompareSalary(Employee e1, Employee e2) { return e1.Salary < e2.Salary; } } }
編寫客戶端代碼,完成我們需要排序的員工數(shù)據(jù):
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Wrox.ProCSharp.Delegates { class Program { static void Main() { Employee[] employees = { new Employee("Bugs Bunny", 20000), new Employee("Elmer Fudd", 10000), new Employee("Daffy Duck", 25000), new Employee("Wile Coyote", 1000000.38m), new Employee("Foghorn Leghorn", 23000), new Employee("RoadRunner", 50000) }; BubbleSorter.Sort(employees, Employee.CompareSalary); foreach (var employee in employees) { Console.WriteLine(employee); } Console.ReadLine(); } } }
排序后我們員工的工資如下:
Elmer Fudd, ¥10,000.00 Bugs Bunny, ¥20,000.00 Foghorn Leghorn, ¥23,000.00 Daffy Duck, ¥25,000.00 RoadRunner, ¥50,000.00 Wile Coyote, ¥1,000,000.38
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國(guó)云服務(wù)器,動(dòng)態(tài)BGP最優(yōu)骨干路由自動(dòng)選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動(dòng)現(xiàn)已開啟,新人活動(dòng)云服務(wù)器買多久送多久。