委托概述
如何聲明委托?
如何使用委托?
Action<T>和Func<T>的使用
多播委托是什么?
匿名方法
委托是尋址方法的.Net版本,是類(lèi)型安全的類(lèi),它定義了返回類(lèi)型和參數(shù)類(lèi)型。委托類(lèi)不僅包含對(duì)單個(gè)方法的引用,也可以包含對(duì)多個(gè)方法的引用。
委托是一個(gè)特殊類(lèi)型的對(duì)象,其特殊之處在于,我們之前定義的所有對(duì)象包含數(shù)據(jù),而委托包含的只是一個(gè)或多個(gè)方法的地址。
委托和lambda表達(dá)式直接相關(guān),當(dāng)參數(shù)是委托的時(shí)候,我們可以使用lamabda表達(dá)式實(shí)現(xiàn)委托引用的方法。
我們都知道在C#中使用一個(gè)類(lèi)的時(shí)候分為兩個(gè)階段。首先,需要定義這個(gè)類(lèi)(告訴編譯器這個(gè)類(lèi)由什么字段和方法組成)。然后實(shí)例化類(lèi)的一個(gè)對(duì)象(靜態(tài)方法除外)。使用委托時(shí),我們也需要經(jīng)過(guò)類(lèi)似以上的兩個(gè)步驟。首先必須定義要使用的委托,對(duì)于委托,定義它就是告訴編譯器這種類(lèi)型的委托表示哪種類(lèi)型的方法。然后,必須創(chuàng)建該委托的一個(gè)或多個(gè)實(shí)例。編譯器在后臺(tái)將創(chuàng)建表示該委托的類(lèi)。
委托的基本語(yǔ)法如下:
delegate void IntMethodInvoker(int x);
注釋:上述基本語(yǔ)法案例是說(shuō)定義了一個(gè)委托IntMethodInvoker,并且指定了該委托的每個(gè)實(shí)例都可以包含一個(gè)方法(該方法帶有一個(gè)int參數(shù))的引用,并且方法返回值是void(空)。理解委托的一個(gè)要點(diǎn)是他們的類(lèi)型安全性非常高。在定義委托時(shí),必須給出它所表示的方法的簽名和返回類(lèi)型等全部細(xì)節(jié)。說(shuō)白了就是給方法的簽名和返回類(lèi)型指定名稱(chēng)。
假如我們有如下需求:有一個(gè)方法需要使用委托,該方法的接收兩個(gè)long型參數(shù),并且返回值是一個(gè)double類(lèi)型,那我我們?yōu)樵摲椒ń⒌奈腥缦拢?/p>
delegate double TwoLongsOp(long first, long second);
注釋?zhuān)浩湔Z(yǔ)法類(lèi)似于方法的定義,但是沒(méi)有方法體。定義的前面需要加上關(guān)鍵字delegate。因?yàn)槎x委托基本上是定義一個(gè)新類(lèi),所以可以在定義類(lèi)的任何相同地方定義委托。也就是說(shuō),可以在另一個(gè)類(lèi)的內(nèi)部定義,也可以在任何類(lèi)的外部定義,還可以在名稱(chēng)空間中把委托定義為頂層對(duì)象。根據(jù)定義的可見(jiàn)性和委托的作用域,可以在委托的定義上應(yīng)用任意常用的訪(fǎng)問(wèn)修飾符:Public、private、protected等。
實(shí)際上,“定義一個(gè)委托”是指“定義一個(gè)新類(lèi)”。委托實(shí)現(xiàn)為派生自基類(lèi)System.MulticastDelegate的類(lèi),System.MulticastDelegate又派生自基類(lèi)System.Delegate.C#編譯器能識(shí)別這個(gè)類(lèi),會(huì)使用其委托語(yǔ)法,因此我們不需要了解這個(gè)類(lèi)的具體執(zhí)行情況。這是C#與基類(lèi)共同合作,使編程更易完成的另一個(gè)范例。
定義好委托后,就可以創(chuàng)建它的一個(gè)實(shí)例,從而用它存儲(chǔ)特定方法的細(xì)節(jié)。
但是,在術(shù)語(yǔ)方面有一個(gè)問(wèn)題,類(lèi)有兩個(gè)不同的術(shù)語(yǔ):“類(lèi)”表示較廣義的定義,“對(duì)象”表示類(lèi)的實(shí)例。但委托只有一個(gè)術(shù)語(yǔ)。在創(chuàng)建委托的實(shí)例,所創(chuàng)建的委托的實(shí)例仍稱(chēng)為委托,必須從上下文中確定委托的確切含義。
下面這段代碼說(shuō)明如何使用委托(簡(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í)例化了類(lèi)型為GetString的一個(gè)委托,并對(duì)它進(jìn)行初始化,使它引用整型變量x的ToString()方法。在C#中,委托在語(yǔ)法上總是接收一個(gè)參數(shù)的構(gòu)造函數(shù),這個(gè)參數(shù)就是委托引用的方法。這個(gè)方法必須匹配最初定義委托時(shí)的簽名。所以在這個(gè)示例中,如果不帶x.ToString來(lái)初始化變量firstStringMethod變量會(huì)報(bào)編譯錯(cuò)誤。注意:因?yàn)閕nt.ToString()是一個(gè)實(shí)例方法(不是靜態(tài)方法),所以需要指定實(shí)例(x)和方法名來(lái)正確地初始化委托。
在上述代碼中的firstStringMethod.Invoke()和firstStringMethod()方法是會(huì)產(chǎn)生完全相同的效果的。Invoke()是委托類(lèi)的方法。因?yàn)閒irstStringMethod是委托類(lèi)型的一個(gè)變量,所以C#編譯器會(huì)用firstStringMethod.Invoke()代替firstStringMethod()。
為了減少輸入量,只要需要委托實(shí)例,就可以只傳送地址名稱(chēng)。這就稱(chēng)為委托推斷。只要編譯器可以把委托實(shí)例解析為特定的類(lèi)型,這個(gè)C#特性就是有效的,所以上面的代碼還有另外一種寫(xiě)法:
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è)特征是它們的類(lèi)型是安全的,可以確保被調(diào)用的方法簽名是正確的。但是它不關(guān)心在什么類(lèi)型的對(duì)象上調(diào)用該方法,甚至不考慮該方法是靜態(tài)方法,還是實(shí)例方法。
首先建立一個(gè)動(dòng)靜態(tài)都包含的Currency.cs類(lèi):
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(); } } }
注釋?zhuān)荷厦嬲f(shuō)明了如何通過(guò)委托調(diào)用動(dòng)靜態(tài)方法,只要每個(gè)方法的簽名匹配委托定義即可。
首先新建一個(gè)包含正方形和長(zhǎng)方形的計(jì)算方法的類(lèi)MathOperation.cs,該類(lèi)包含動(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; } } }
然后建立委托來(lái)調(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
注釋?zhuān)涸谶@段代碼中,實(shí)例化了一個(gè)DoubleOp委托數(shù)組(需要我們了解的是,一旦定義了委托類(lèi),基本上就可以實(shí)例化它的實(shí)例,就像處理一般的類(lèi)那樣--所以把一些委托的實(shí)例放在數(shù)組中是可以的)。該數(shù)組的每個(gè)元素都初始化為由MathOperation類(lèi)實(shí)現(xiàn)的不同操作。然后遍歷這個(gè)數(shù)組,每個(gè)操作應(yīng)用到三個(gè)不同的值上。這就就說(shuō)了使用委托的一種方式--把方法組合到一個(gè)數(shù)組中來(lái)使用,這樣就可以在循環(huán)中調(diào)用不同的方法了。
簡(jiǎn)單案例的代碼解析:
這段代碼的最為關(guān)鍵的一行是把每個(gè)委托傳遞給ProcessAndDisplayNumber方法,比如:
ProcessAndDisplayNumber(operations[i], 2.0);
其中傳遞了委托名,但不帶任何參數(shù)。其語(yǔ)法是:
1、operations[i]表示“這個(gè)委托”,也可以說(shuō)成是委托表示的方法。
2、2.0表示的是一個(gè)參數(shù)。
3、然后調(diào)用 double result = action(value);這個(gè)實(shí)際上是調(diào)用委托實(shí)例封裝的方法。其返回結(jié)果存儲(chǔ)在result中。
除了為每個(gè)參數(shù)和返回類(lèi)型定義一個(gè)新委托類(lèi)型之外,還可以使用Action<T>和Func<T>委托。
泛型Action<T>委托表示引用一個(gè)void返回類(lèi)型的方法。這個(gè)委托類(lèi)存在不同的變體,可以傳遞至多16種不同的參數(shù)類(lèi)型。沒(méi)有泛型參數(shù)的Action類(lèi)可調(diào)用沒(méi)有參數(shù)的方法。Action<in T>調(diào)用帶一個(gè)參數(shù)的方法,Action<int T1, in T2> 調(diào)用帶兩個(gè)參數(shù)的方法,以此類(lèi)推。
Func<T>委托可以以類(lèi)似的方法使用。Func<T>允許調(diào)用帶返回類(lèi)型的方法,并且也定義了不同的變體,也是至多傳遞16個(gè)參數(shù)類(lèi)型和一個(gè)返回類(lèi)型。Func<out TResult>委托類(lèi)型可以調(diào)用帶返回類(lèi)型且無(wú)參數(shù)的方法,F(xiàn)unc<in T, out TResult>調(diào)用帶一個(gè)參數(shù)的方法,以此類(lèi)推,注意返回類(lèi)型只能是一個(gè)。
因此利用Func<T>委托可以有如下的調(diào)用方法:
using System; namespace Wrox.ProCSharp.Delegates { class Program { static void Main() { Func<double, double>[] 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<double, double> 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è)方法的委托就被稱(chēng)為多播委托。如果調(diào)用多播委托,就可以按順序連續(xù)調(diào)用多個(gè)方法。為此,委托簽名就必須返回void。否則,就只能得到委托調(diào)用的最后一個(gè)方法結(jié)果。
我們明顯可以利用的是返回類(lèi)型為void的Action<double>委托:
首先建立一MathOperations.cs類(lèi):
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() { Action<double> operations = 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<double> action, double value) { Console.WriteLine(); Console.WriteLine("ProcessAndDispalyNumber called with value ={0}", value); action(value); } } }
注釋?zhuān)憾嗖ノ锌梢宰R(shí)別運(yùn)算符“+”和“+=”、“-”和“-=”。多播委托將兩個(gè)執(zhí)行方法通過(guò)“+”和“+=”連在一起(叫做擴(kuò)展),還可以利用“-”和“-=”刪除方法調(diào)用。多播實(shí)際上是一個(gè)派生自System.MulticastDelegate的類(lèi),System.MuticastDelegate又派生自基類(lèi)System.Delegate。System.MulticastDelegate的其他成員允許把多個(gè)方法調(diào)用鏈接一個(gè)列表。
注意:如果使用多播委托,我們需要了解對(duì)同一個(gè)委托調(diào)用方法鏈的順序并未正式定義。因此應(yīng)避免依賴(lài)于以特定順序調(diào)用方法代碼。還有個(gè)問(wèn)題是多播委托包含一個(gè)逐個(gè)調(diào)用的委托集合。如果通過(guò)委托調(diào)用其中的一個(gè)方法拋出異常,則整個(gè)迭代都會(huì)停止。
下面將展示如果多播委托第一個(gè)拋出錯(cuò)誤第二個(gè)方法無(wú)法正常執(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"); } } } }
注釋?zhuān)簅ne()方法我們故意加了個(gè)異常問(wèn)題,那么執(zhí)行Main()的時(shí)候會(huì)自動(dòng)的在One()方法執(zhí)行的時(shí)候進(jìn)入catch
我們?nèi)绻鉀Q上面的這種多播委托存在的問(wèn)題的,我們可以使用Delegate定義GetInvocationList()方法,返回一個(gè)Delegate對(duì)象數(shù)組。然后循環(huán)迭代方法列表,捕獲異常進(jìn)行下次迭代。下面代碼是重寫(xiě)后的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,"; Func<string, string> anonDel = delegate(string param) { param += mid; param += " and this was added to the string."; return param; }; Console.WriteLine(anonDel("Start of string")); } } }
注釋:Func<string,string>委托接收一個(gè)字符串,返回一個(gè)字符串。anonDel是這種委托類(lèi)型的變量。不是把方法名賦予給這個(gè)變量,而是使用一段簡(jiǎn)單的代碼:它前面的關(guān)鍵字delegate,后面是一個(gè)字符串參數(shù)。該代碼塊使用方法級(jí)的字符串變量mid,該變量是匿名方法的外部定義的,并把它添加到要傳遞的參數(shù)中。接著代碼返回該字符串。在調(diào)用委托時(shí),把一個(gè)字符串作為參數(shù)傳遞,將返回的字符串輸出輸出控制臺(tái)上。匿名方法的優(yōu)點(diǎn)是減少了要編寫(xiě)的代碼。不必定義僅由委托使用的方法。在為事件定義委托時(shí),這是非常明顯的。這有助于降低代碼的復(fù)雜性。尤其是定義了好幾個(gè)事件時(shí),代碼會(huì)顯得比較簡(jiǎn)單。代碼的執(zhí)行速度并沒(méi)有加快。
在使用匿名方法的時(shí)候,必須遵循的原則有兩條:一是不能使用跳轉(zhuǎn)語(yǔ)句(break、go或continue)跳轉(zhuǎn)到該匿名方法的外部,反之亦然:匿名方法外部的跳轉(zhuǎn)語(yǔ)句不能跳轉(zhuǎn)到該匿名方法的內(nèi)部。二是在匿名方法的內(nèi)部不能訪(fǎng)問(wèn)不安全的代碼,另外也不能訪(fǎng)問(wèn)匿名方法外部使用的ref和out參數(shù)。但可以使用在匿名方法外部定義的其他變量。
注意:如果需要用匿名方法多次編寫(xiě)同一個(gè)功能,就不要使用匿名方法了。此時(shí)與復(fù)制代碼相比,編寫(xiě)一個(gè)命名方法比較好,因?yàn)樵摲椒ㄖ恍枰帉?xiě)一次,以后可以通過(guò)名稱(chēng)引用它。
為了說(shuō)明委托的實(shí)際用途。我們編寫(xiě)一個(gè)類(lèi)BubbleSorter,它實(shí)現(xiàn)一個(gè)靜態(tài)方法Sort(),這個(gè)方法的第一參數(shù)是一個(gè)對(duì)象數(shù)組,把該數(shù)組按照升序重新排列。
需求是本公司員工的工資需要按照需要的順序進(jìn)行排序:
首先定義一個(gè)BubbleSorter類(lèi):
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Wrox.ProCSharp.Delegates { class BubbleSorter { static public void Sort<T>(IList<T> sortArray, Func<T, T, bool> 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); } } }
定義員工類(lèi),該員工類(lèi)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; } } }
編寫(xiě)客戶(hù)端代碼,完成我們需要排序的員工數(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,專(zhuān)業(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)已開(kāi)啟,新人活動(dòng)云服務(wù)器買(mǎi)多久送多久。
網(wǎng)頁(yè)題目:N001-認(rèn)知C#中的委托-創(chuàng)新互聯(lián)
網(wǎng)站網(wǎng)址:http://chinadenli.net/article0/djgpoo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、企業(yè)建站、網(wǎng)站營(yíng)銷(xiāo)、營(yíng)銷(xiāo)型網(wǎng)站建設(shè)、網(wǎng)站制作、App開(kāi)發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容
網(wǎng)頁(yè)設(shè)計(jì)公司知識(shí)