委托概述
成都創(chuàng)新互聯(lián)企業(yè)建站,10余年網(wǎng)站建設經(jīng)驗,專注于網(wǎng)站建設技術,精于網(wǎng)頁設計,有多年建站和網(wǎng)站代運營經(jīng)驗,設計師為客戶打造網(wǎng)絡企業(yè)風格,提供周到的建站售前咨詢和貼心的售后服務。對于成都網(wǎng)站建設、網(wǎng)站設計中不同領域進行深入了解和探索,創(chuàng)新互聯(lián)在網(wǎng)站建設中充分了解客戶行業(yè)的需求,以靈動的思維在網(wǎng)頁中充分展現(xiàn),通過對客戶行業(yè)精準市場調(diào)研,為客戶提供的解決方案。如何聲明委托?
如何使用委托?
Action<T>和Func<T>的使用
多播委托是什么?
匿名方法
委托是尋址方法的.Net版本,是類型安全的類,它定義了返回類型和參數(shù)類型。委托類不僅包含對單個方法的引用,也可以包含對多個方法的引用。
委托是一個特殊類型的對象,其特殊之處在于,我們之前定義的所有對象包含數(shù)據(jù),而委托包含的只是一個或多個方法的地址。
委托和lambda表達式直接相關,當參數(shù)是委托的時候,我們可以使用lamabda表達式實現(xiàn)委托引用的方法。
我們都知道在C#中使用一個類的時候分為兩個階段。首先,需要定義這個類(告訴編譯器這個類由什么字段和方法組成)。然后實例化類的一個對象(靜態(tài)方法除外)。使用委托時,我們也需要經(jīng)過類似以上的兩個步驟。首先必須定義要使用的委托,對于委托,定義它就是告訴編譯器這種類型的委托表示哪種類型的方法。然后,必須創(chuàng)建該委托的一個或多個實例。編譯器在后臺將創(chuàng)建表示該委托的類。
委托的基本語法如下:
delegate void IntMethodInvoker(int x);
注釋:上述基本語法案例是說定義了一個委托IntMethodInvoker,并且指定了該委托的每個實例都可以包含一個方法(該方法帶有一個int參數(shù))的引用,并且方法返回值是void(空)。理解委托的一個要點是他們的類型安全性非常高。在定義委托時,必須給出它所表示的方法的簽名和返回類型等全部細節(jié)。說白了就是給方法的簽名和返回類型指定名稱。
假如我們有如下需求:有一個方法需要使用委托,該方法的接收兩個long型參數(shù),并且返回值是一個double類型,那我我們?yōu)樵摲椒ń⒌奈腥缦拢?/p>
delegate double TwoLongsOp(long first, long second);
注釋:其語法類似于方法的定義,但是沒有方法體。定義的前面需要加上關鍵字delegate。因為定義委托基本上是定義一個新類,所以可以在定義類的任何相同地方定義委托。也就是說,可以在另一個類的內(nèi)部定義,也可以在任何類的外部定義,還可以在名稱空間中把委托定義為頂層對象。根據(jù)定義的可見性和委托的作用域,可以在委托的定義上應用任意常用的訪問修飾符:Public、private、protected等。
實際上,“定義一個委托”是指“定義一個新類”。委托實現(xiàn)為派生自基類System.MulticastDelegate的類,System.MulticastDelegate又派生自基類System.Delegate.C#編譯器能識別這個類,會使用其委托語法,因此我們不需要了解這個類的具體執(zhí)行情況。這是C#與基類共同合作,使編程更易完成的另一個范例。
定義好委托后,就可以創(chuàng)建它的一個實例,從而用它存儲特定方法的細節(jié)。
但是,在術語方面有一個問題,類有兩個不同的術語:“類”表示較廣義的定義,“對象”表示類的實例。但委托只有一個術語。在創(chuàng)建委托的實例,所創(chuà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(); } } }
在這段代碼中,實例化了類型為GetString的一個委托,并對它進行初始化,使它引用整型變量x的ToString()方法。在C#中,委托在語法上總是接收一個參數(shù)的構造函數(shù),這個參數(shù)就是委托引用的方法。這個方法必須匹配最初定義委托時的簽名。所以在這個示例中,如果不帶x.ToString來初始化變量firstStringMethod變量會報編譯錯誤。注意:因為int.ToString()是一個實例方法(不是靜態(tài)方法),所以需要指定實例(x)和方法名來正確地初始化委托。
在上述代碼中的firstStringMethod.Invoke()和firstStringMethod()方法是會產(chǎn)生完全相同的效果的。Invoke()是委托類的方法。因為firstStringMethod是委托類型的一個變量,所以C#編譯器會用firstStringMethod.Invoke()代替firstStringMethod()。
為了減少輸入量,只要需要委托實例,就可以只傳送地址名稱。這就稱為委托推斷。只要編譯器可以把委托實例解析為特定的類型,這個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)用上述方法名時輸入形式不能為x.ToString()(不要輸入圓括號),也不能把它傳送給委托變量。輸入圓括號調(diào)用一個方法。調(diào)用x.ToString()方法會返回一個不能賦予委托變量的字符串對象。只能把方法的地址賦予委托變量。
委托的一個特征是它們的類型是安全的,可以確保被調(diào)用的方法簽名是正確的。但是它不關心在什么類型的對象上調(diào)用該方法,甚至不考慮該方法是靜態(tài)方法,還是實例方法。
首先建立一個動靜態(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實例:
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)用動靜態(tài)方法,只要每個方法的簽名匹配委托定義即可。
首先新建一個包含正方形和長方形的計算方法的類MathOperation.cs,該類包含動靜態(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); } } }
運行結果:
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
注釋:在這段代碼中,實例化了一個DoubleOp委托數(shù)組(需要我們了解的是,一旦定義了委托類,基本上就可以實例化它的實例,就像處理一般的類那樣--所以把一些委托的實例放在數(shù)組中是可以的)。該數(shù)組的每個元素都初始化為由MathOperation類實現(xiàn)的不同操作。然后遍歷這個數(shù)組,每個操作應用到三個不同的值上。這就就說了使用委托的一種方式--把方法組合到一個數(shù)組中來使用,這樣就可以在循環(huán)中調(diào)用不同的方法了。
簡單案例的代碼解析:
這段代碼的最為關鍵的一行是把每個委托傳遞給ProcessAndDisplayNumber方法,比如:
ProcessAndDisplayNumber(operations[i], 2.0);
其中傳遞了委托名,但不帶任何參數(shù)。其語法是:
1、operations[i]表示“這個委托”,也可以說成是委托表示的方法。
2、2.0表示的是一個參數(shù)。
3、然后調(diào)用 double result = action(value);這個實際上是調(diào)用委托實例封裝的方法。其返回結果存儲在result中。
除了為每個參數(shù)和返回類型定義一個新委托類型之外,還可以使用Action<T>和Func<T>委托。
泛型Action<T>委托表示引用一個void返回類型的方法。這個委托類存在不同的變體,可以傳遞至多16種不同的參數(shù)類型。沒有泛型參數(shù)的Action類可調(diào)用沒有參數(shù)的方法。Action<in T>調(diào)用帶一個參數(shù)的方法,Action<int T1, in T2> 調(diào)用帶兩個參數(shù)的方法,以此類推。
Func<T>委托可以以類似的方法使用。Func<T>允許調(diào)用帶返回類型的方法,并且也定義了不同的變體,也是至多傳遞16個參數(shù)類型和一個返回類型。Func<out TResult>委托類型可以調(diào)用帶返回類型且無參數(shù)的方法,F(xiàn)unc<in T, out TResult>調(diào)用帶一個參數(shù)的方法,以此類推,注意返回類型只能是一個。
因此利用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)用多個方法,就需要多次顯示調(diào)用這個委托。但是,委托也可以包含多個方法。這種包含多個方法的委托就被稱為多播委托。如果調(diào)用多播委托,就可以按順序連續(xù)調(diào)用多個方法。為此,委托簽名就必須返回void。否則,就只能得到委托調(diào)用的最后一個方法結果。
我們明顯可以利用的是返回類型為void的Action<double>委托:
首先建立一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)用當前方法的委托函數(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); } } }
注釋:多播委托可以識別運算符“+”和“+=”、“-”和“-=”。多播委托將兩個執(zhí)行方法通過“+”和“+=”連在一起(叫做擴展),還可以利用“-”和“-=”刪除方法調(diào)用。多播實際上是一個派生自System.MulticastDelegate的類,System.MuticastDelegate又派生自基類System.Delegate。System.MulticastDelegate的其他成員允許把多個方法調(diào)用鏈接一個列表。
注意:如果使用多播委托,我們需要了解對同一個委托調(diào)用方法鏈的順序并未正式定義。因此應避免依賴于以特定順序調(diào)用方法代碼。還有個問題是多播委托包含一個逐個調(diào)用的委托集合。如果通過委托調(diào)用其中的一個方法拋出異常,則整個迭代都會停止。
下面將展示如果多播委托第一個拋出錯誤第二個方法無法正常執(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()方法我們故意加了個異常問題,那么執(zhí)行Main()的時候會自動的在One()方法執(zhí)行的時候進入catch
我們?nèi)绻鉀Q上面的這種多播委托存在的問題的,我們可以使用Delegate定義GetInvocationList()方法,返回一個Delegate對象數(shù)組。然后循環(huá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表達式代替匿名函數(shù)。我們熟悉的是要想使用委托工作,方法必須是已經(jīng)存在的。但是還有另外一種使用委托的方式:匿名方法
實際上匿名方法與實例委托的區(qū)別只是在實例化委托的時候,那么我們首先先看下面的代碼是如何使用匿名方法的:
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>委托接收一個字符串,返回一個字符串。anonDel是這種委托類型的變量。不是把方法名賦予給這個變量,而是使用一段簡單的代碼:它前面的關鍵字delegate,后面是一個字符串參數(shù)。該代碼塊使用方法級的字符串變量mid,該變量是匿名方法的外部定義的,并把它添加到要傳遞的參數(shù)中。接著代碼返回該字符串。在調(diào)用委托時,把一個字符串作為參數(shù)傳遞,將返回的字符串輸出輸出控制臺上。匿名方法的優(yōu)點是減少了要編寫的代碼。不必定義僅由委托使用的方法。在為事件定義委托時,這是非常明顯的。這有助于降低代碼的復雜性。尤其是定義了好幾個事件時,代碼會顯得比較簡單。代碼的執(zhí)行速度并沒有加快。
在使用匿名方法的時候,必須遵循的原則有兩條:一是不能使用跳轉語句(break、go或continue)跳轉到該匿名方法的外部,反之亦然:匿名方法外部的跳轉語句不能跳轉到該匿名方法的內(nèi)部。二是在匿名方法的內(nèi)部不能訪問不安全的代碼,另外也不能訪問匿名方法外部使用的ref和out參數(shù)。但可以使用在匿名方法外部定義的其他變量。
注意:如果需要用匿名方法多次編寫同一個功能,就不要使用匿名方法了。此時與復制代碼相比,編寫一個命名方法比較好,因為該方法只需要編寫一次,以后可以通過名稱引用它。
為了說明委托的實際用途。我們編寫一個類BubbleSorter,它實現(xiàn)一個靜態(tài)方法Sort(),這個方法的第一參數(shù)是一個對象數(shù)組,把該數(shù)組按照升序重新排列。
需求是本公司員工的工資需要按照需要的順序進行排序:
首先定義一個BubbleSorter類:
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); } } }
定義員工類,該員工類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è)提供香港、美國云服務器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡助力業(yè)務部署。公司持有工信部辦法的idc、isp許可證, 機房獨有T級流量清洗系統(tǒng)配攻擊溯源,準確進行流量調(diào)度,確保服務器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務器買多久送多久。
網(wǎng)頁題目:N001-認知C#中的委托-創(chuàng)新互聯(lián)
網(wǎng)站網(wǎng)址:http://muchs.cn/article0/djgpoo.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供Google、企業(yè)建站、網(wǎng)站營銷、營銷型網(wǎng)站建設、網(wǎng)站制作、App開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)