標籤:run 功能 一個 問題 加速 employee false opera compare
委託
- 如果我們要把方法當做參數來傳遞,就用到委託,簡單來說,委託是一個類型,這個類型可以賦值一個方法的引用。
聲明委託
委託使用
private delegate string GetAString();static void Main(){int x = 40;GetAString firstStringMethod = new GetAString(x.ToString);Console.WriteLine(firstStringMethod());}
委託的賦值
GetAString firstStringMethod = new GetAString(x.ToString);
- 只需要把方法名給一個委託的構造方法就可以了
GetAString firstStringMethod = x.ToString;
- 也可以把方法名直接給委託的執行個體
簡單委託樣本
- 定義一個類MathsOperations裡面有兩個靜態方法,使用委託調用該方法
class MathOperations{public static double MultiplyByTwo(double value){return value*2;}public static double Square(double value){return value*value;}}delegate double DoubleOp(double x);static void Main(){DoubleOp[] operations={ MathOperations.MultiplyByTwo,MathOperations.Square };for(int i =0;i<operations.Length;i++){Console.WriteLine("Using operations "+i);ProcessAndDisplayNumber( operations[i],2.0 );}}static void ProcessAndDisplayNumber(DoubleOp action,double value){double res = action(value);Console.Writeline("Value :"+value+" Result:"+res);}
Action委託和Func委託
- 除了我們自己定義的委託之外,系統還給我們提供過來一個內建的委託類型
Action和Func
Action委託引用了一個void傳回型別的方法,T表示方法參數
- 先看
Action委託有哪些
- Action
- Action<in T>
- Action<in T1,in T2>
- Action<in T1,in T2 .... inT16>
Func引用了一個帶有一個傳回值的方法,它可以傳遞0或者多到16個參數類型,和一個傳回型別
- Func<out TResult>
- Func<in T,out TResult>
- Func<int T1,inT2,,,,,,in T16,out TResult>
案例1
案例2-對int類型排序
bool swapped = true;do{swapped = false;for(int i =0;i<sortArray.Length -1;i++){if(sortArray[i]>sortArray[i+1]){int temp= sortArray[i];sortArray[i]=sortArray[i+1];sortArray[i+1]=temp;swapped = true;}}}while(swapped);
- 這裡的冒泡排序只適用於int類型的,如果我們想對他進行擴充,這樣它就可以給任何對象排序。
案例2-僱員類
class Employee{public Employ(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;}}
案例2-通用的排序方法
public static void Sort<T>( List<T> sortArray,Func<T,T,bool> comparision ){bool swapped = true;do{swapped = false;for(int i=0;i<sortArray.Count-1;i++){if(comparision(sortArray[i+1],sortArray[i])){T temp = sortArray[i];sortArray[i]=sortArray[i+1];sortArray[i+1]=temp;swapped = true;}}}while(swapped);}
案例2-對僱員類排序
static void Main(){Employee[] employees = {new Employee("Bunny",20000),new Employee("Bunny",10000),new Employee("Bunny",25000),new Employee("Bunny",100000),new Employee("Bunny",23000),new Employee("Bunny",50000),};Sort(employees,Employee.CompareSalary);// 輸出}
多播委託
Action a1 = Method1;a1+=Method2;Delegate[] delegates=a1.GetInvocationList();foreach(delegate d in delegates){//d();d.DynamicInvoke(null);}// 遍曆多播委託中所有的委託,然後單獨調用
匿名方法
- 到目前為止,使用委託,都是先定義一個方法,然後把方法給委託的執行個體。但還有另外一種使用委託的方式,不用去定義一個方法,應該說是使用匿名方法(方法沒有名字)。
Func<int,int,int> plus = delegate (int a,int b){int temp = a+b;return temp;};int res = plus(34,34);Console.WriteLine(res);
- 在這裡相當於直接把要引用的方法直接寫在了後面,優點是減少了要編寫的代碼,減少代碼的複雜性
Lambda運算式-表示一個方法的定義
- 從C#3.0開始,可以使用Lambda運算式代替匿名方法。只要有委託參數類型的地方就可以使用Lambda運算式。剛剛的例子可以修改為
Func<int,int,int> plus = (a,b)=>{ int temp= a+b;return temp; }; int res = plus(34,34); Console.WriteLine(res);
- Lambda運算子“=>”的左邊列出了需要的參數,如果是一個參數可以直接寫 a=>(參數名自己定義),如果多個參數就使用括弧括起來,參數之間以,間隔
多行語句
- 如果Lambda運算式只有一條語句,在方法快內就不需要花括弧和return語句,編譯器會自動添加return語句
Func<double,double> square = x=>x*x;// 添加花括弧,return語句和分號是完全合法的Func<double,double> square = x=>{return x*x;}
- 如果Lambda運算式的實現代碼中需要多條語句,就必須添加花括弧和return語句。
Lambda運算式外部的變數
- 通過Lambda運算式可以訪問Lambda運算式塊外部的變數。這是一個非常好的功能,但如果不能正確使用,也會非常危險。樣本:
int somVal = 5;Func<int,int> f = x=>x+somVal;Console.WriteLine(f(3));//8somVal = 7;Console.WriteLine(f(3));//10
- 這個方法的結果,不但受到參數的控制,還受到somVal變數的控制,結果不可控,容易出現編程問題,用的時候要謹慎。
事件
- 事件(event)基於委託,為委託提供了一個發布/訂閱機制,我們可以說事件是一種具有特殊簽名的委託。 什麼是事件? 事件(Event)是類或對象向其他類或對象通知發生的事情的一種特殊簽名的委託. 事件的聲明 public event 委託類型 事件名; 事件使用event關鍵詞來聲明,他的返回類值是一個委託類型。 通常事件的命名,以名字+Event 作為他的名稱,在編碼中盡量使用規範命名,增加代碼可讀性。
- 為了更加容易理解事件,我們還是以前面的動物的樣本來說明,有三隻動物,貓(名叫Tom),還有兩隻老鼠(Jerry和Jack),當貓叫的時候,觸發事件(CatShout),然後兩隻老鼠開始逃跑(MouseRun)。接下來用代碼來實現。(設計模式-觀察者模式)
Cat類和Mouse類
class Cat { string catName; string catColor { get; set; } public Cat(string name, string color) { this.catName = name; catColor = color; } public void CatShout() { Console.WriteLine(catColor+" 的貓 "+catName+" 過來了,喵!喵!喵!\n"); //貓叫時觸發事件 //貓叫時,如果CatShoutEvent中有登記事件,則執行該事件 if (CatShoutEvent != null) CatShoutEvent(); } public delegate void CatShoutEventHandler(); public event CatShoutEventHandler CatShoutEvent; } class Mouse { string mouseName; string mouseColor { get; set; } public Mouse(string name, string color) { this.mouseName = name; this.mouseColor = color; } public void MouseRun() { Console.WriteLine(mouseColor + " 的老鼠 " + mouseName + " 說:\"老貓來了,快跑!\" \n我跑!!\n我使勁跑!!\n我加速使勁跑!!!\n"); } }
運行代碼結果
Console.WriteLine("[情境說明]: 一個月明星稀的午夜,有兩隻老鼠在偷油吃\n"); Mouse Jerry = new Mouse("Jerry", "白色"); Mouse Jack = new Mouse("Jack", "黃色"); Console.WriteLine("[情境說明]: 一隻黑貓躡手躡腳的走了過來\n"); Cat Tom = new Cat("Tom", "黑色"); Console.WriteLine("[情境說明]: 為了安全的偷油,登記了一個貓叫的事件\n"); Tom.CatShoutEvent += new Cat.CatShoutEventHandler(Jerry.MouseRun); Tom.CatShoutEvent += new Cat.CatShoutEventHandler(Jack.MouseRun); Console.WriteLine("[情境說明]: 貓叫了三聲\n"); Tom.CatShout(); Console.ReadKey();
事件與委託的聯絡和區別
事件是一種特殊的委託,或者說是受限制的委託,是委託一種特殊應用,只能施加+=,-=操作符。二者本質上是一個東西。
event ActionHandler Tick; // 編譯成建立一個私人的委託樣本, 和施加在其上的add, remove方法.
event只允許用add, remove方法來操作,這導致了它不允許在類的外部被直接觸發,只能在類的內部適合的時機觸發。委託可以在外部被觸發,但是別這麼用。
使用中,委託常用來表達回調,事件表達外發的介面。
委託和事件支援靜態方法和成員方法, delegate(void * pthis, f_ptr), 支援靜態返方法時, pthis傳null.支援成員方法時, pthis傳被通知的對象.
委派物件裡的三個重要欄位是, pthis, f_ptr, pnext, 也就是被通知對象引用, 函數指標/地址, 委託鏈表的下一個委託節點.
C# 委託