1 委托
當要把方法傳遞給其他方法時,需要使用委托。有時候某個方法執行的操作并不是針對數據進行的,而是要對另一個方法進行操作。但是編譯的時候,我們不知道第二個方法是什么,這個信息只有在運行時得到,所以需要我們把第二個方法作為參數傳遞給第一個方法??傊芯褪菂禐榉椒ǖ暮瘮?。
事件——一般是通知代碼發生了什么事件。引發了事件時,運行庫需要知道應該執行哪個方法。這就要把處理事件的方法作為一個參數傳遞給委托。
1.1 聲明委托
首先必須定義要使用的委托,對于委托,定義它就是告訴編譯器這種類型的委托表示哪種類型的方法,然后必須創建該委托的一個或者多個實例。編譯器在后臺將創建表示該委托的一個類。語法如下:
delegate void IntMethodInvoker(int x);
在這個實例中,定義了一個委托IntMethodInvoker,并指定該委托的每個實例都可以包含一個方法的引用,該方法含有一個int參數,返回void。定義一個委托,必須給出它所表示的方法的簽名和返回類型。定義委托的語法類似方法的定義,但是沒有方法體,定義的前面要加上關鍵字delegate。可以在委托的定義上應用常用的訪問修飾符:public、PRivate、protected等。
定義委托實際上是定義一個新類。委托實現為派生自基類System.MulticastDelegate的類,System.MulticastDelegate又派生自基類System.Delegate。C#編譯器能識別這個類自動使用其委托語法,我們不需要了解這個類的具體執行情況。
定義好委托后就可以創建它的一個實例,但創建的委托的實例仍然稱為委托。
1.2 使用委托
下面代碼說明如何使用委托,這是在int上調用ToString()方法的一種冗長方式。
private delegate string GetAString();//定義委托,表示的方法不帶參數,返回一個string型的值。 static void Main() { int x=40; GetAString firstStringMethod=new GetAString(x.TOString);//實例化類型為GetAString的委托,并對它進行初始化,//使它引用整形變量x的ToString()方法。在C#中,委托在語法上總是接受一個參數的構造函數,這個參數就是委托引用的方法。//這個方法必須匹配最初定義委托時的簽名。 Console.WriteLine("String is {0}",firstStringMethod()); //使用委托顯示字符串//在任何代碼中,都應該提供委托實例的名稱,這里例子實例名稱為firstStringMethod,后面括號中應該包含調用委托中方法時使用的任何等效參數 }
在C#編譯時,編譯器會用firstString.Method.Invoke()代替firstStringMethod()。(這是IDE內部實現的,不需要編程者關心,這里只是說明下它的原理。)
為了減少代碼,只需要委托實例,就可以只傳送地址的名稱,這稱為委托推斷。舉例如下:
GetAString firstStringMethod =new GetAString(x.ToString);//正常用法
GetAString firstStringMethod=x.ToString;//委托推斷的用法
注意:千萬不能輸入x.ToString()。因為加()的方法是返回一個字符串對象,不加括號代表方法地址,委托調用的是方法地址。
委托的類型是安全的,可以保證被調用的方法簽名正確。
1.3 Action<T>和Func<T>委托
以上都是為參數和返回類型定義一個新委托。下面介紹Action<T>委托,泛型Action<T>委托表示引用一個void返回類型方法。Func<T>委托可以調用帶返回類型的方法。Func<out TResult>委托類型可以調用帶返回類型且無參數的方法,Func<int T,out TResult>調用一個帶參數的方法。
舉例聲明一個該委托類型的數組:
Func<double ,double>[] Operations=
{
類名.方法名,類名.方法名
}
1.4 一個委托的例子:類的冒泡法排序
class BubbleSorter{ static public void Sort<T>(IList<T> sortArray,Func<T,T,bool>comparison)//接受類型T的泛型方法Sort<T> { 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); }} 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 static bool CompareSalary(Employee e1,Employee e2) { return e1.Salary<e2.Salary; }}
Main函數調用時:
static void Main(){ Employee[] employees={ new Employee("Buges",2000), new Employee("Daffy",1000), new Employee("Foge",2300), new Employee("Tows",5000), new Employee("Gay",2100)};BubbleSorter.Sort(employees,Employee.ConpareSalary);//調用類的靜態方法}
1.5多播委托
委托可以包含多個方法,那就是多播委托。為此委托的簽名必須返回void;否則就只能得到委托調用的最后一個方法的結果。多播委托可以識別運算符”+“和”+=“。
Action<double>operation1=類名1.方法名1;
Action<double>operation2=類名2.方法名2;
Action<double>operationS=operation1+operation2;
多播委托可以用”-“和”-=“以從委托中刪除方法調用。
多播委托派生自System.MulticastDelegate的類,System.MulticastDelegate又派生自基類System.Delegate。MulticastDelegate的其他成員允許把多個方法調用鏈接成一個列表。
多播委托要避免依賴于特定順序的代碼。
如果通過委托調用其中一個方法拋出異常,整個迭代就停止了。為了避免這個問題,Delegate類定義了GetInvocationList()方法,它返回一個Delegate對象數組。先中科院使用這個委托調用與委托直接相關的方法,捕獲異常并繼續下一次迭代。
Action d1= One;d1+=Two;Delegate[] delegates=d1.GetInvocationList();foreach (Action d in delegates){ try { d(); } catch(Exception) { Console.WriteLine("Exception caught"); }}
1.6 Lambda表達式
只要有委托參數類型的地方,就可以使用Lambda表達式。簽名使用匿名方法的例子可以改為使用Lambda表達式。Lambda運算符”=>“的左邊列出了需要的參數。Lambda運算符右邊定義了賦予lambda變量的方法的實現代碼。
Func<string,string>oneParam=s=>String.Format("change uppercase{0}",s.ToUpper());Console.WriteLine(oneParam("test"));
1.6.1 參數
Func<double,double,double>twoParamsWithTypes=(double x,double y)=>x*y;Console.WriteLine(twoParamsWithTypes(4,2));
1.6.2 多行代碼
Func<double,double>square=x=>x*x;
等價于
Func<double,double>square=x=>{ return x*x;}
添加多行語句要有括號和return語句
Func<string,string>lambda=param=>{ param+=mid; param+=" and this was added to the string."; return param;}
新聞熱點
疑難解答