若是咱們要把方法當作參數來傳遞的話,就要用到委託。簡單來講委託是一個類型,這個類型能夠賦值一個方法的引用。編程
在C#中使用一個類分兩個階段,首選定義這個類,告訴編譯器這個類由什麼字段和方法組成的,而後使用這個類實例化對象。在咱們使用委託的時候,也須要通過這兩個階段,首先定義委託,告訴編譯器咱們這個委託能夠指向哪些類型的方法,而後,建立該委託的實例。設計模式
定義委託的語法以下: 函數
delegate void IntMethodInvoker(int x);
定義了一個委託叫作IntMethodInvoker, 這個方法要帶有一個int類型的參數,而且方法的返回值是void的。 定義一個委託要定義方法的參數和返回值,使用關鍵字delegate定義。this
private delegate string GetAString(); static void Main(){ int x = 40; GetAString firstStringMethod = new GetAString(x.ToString); Console.WriteLine(firstStringMethod()); }
在這裏咱們首先使用GetAString委託聲明瞭一個類型叫作fristStringMethod,接下來使用new 對它進行初始化,使它引用到x中的ToString方法上,這樣firstStringMethod就至關於x.ToString,咱們經過firstStringMethod()執行方法就至關於x.ToString()spa
經過委託示例調用方法有兩種方式設計
fristStringMethod(); firstStringMethod.Invoke();
只須要把方法名給一個委託的構造方法就能夠了code
GetAString firstStringMethod = new GetAString(x.ToString);
也能夠把方法名直接給委託的實例對象
GetAString firstStringMethod = x.ToString;
除了咱們本身定義的委託以外,系統還給咱們提供過來一個內置的委託類型,Action和Func事件
Action委託引用了一個void返回類型的方法,T表示方法參數編譯器
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _005_Action委託 { class Program { static void PrintString() { Console.WriteLine("hello world."); } static void PrintInt(int i) { Console.WriteLine(i); } static void PrintString(string str) { Console.WriteLine(str); } static void PrintDoubleInt(int i1, int i2) { Console.WriteLine(i1+i2); } static void Main(string[] args) { //Action a = PrintString;//action是系統內置(預約義)的一個委託類型,它能夠指向一個沒有返回值,沒有參數的方法 //Action<int> a=PrintInt;//定義了一個委託類型,這個類型能夠指向一個沒有返回值,有一個int參數的方法 //Action<string> a = PrintString;//定義了一個委託類型,這個類型能夠指向一個沒有返回值,有一個string參數的方法 在這裏系統會自動尋找匹配的方法 Action<int, int> a = PrintDoubleInt; a(34, 23); Console.ReadKey(); //action能夠後面經過泛型去指定action指向的方法的多個參數的類型 ,參數的類型跟action後面聲明的委託類型是對應着的 } } }
Func引用了一個帶有一個返回值的方法,它能夠傳遞0或者多到16個參數類型,和一個返回類型
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _006_Func委託 { class Program { static int Test1() { return 1; } static int Test2(string str) { Console.WriteLine(str); return 100; } static int Test3(int i, int j) { return i + j; } static void Main(string[] args) { //Func<int> a = Test1;//func中的泛型類型制定的是 方法的返回值類型 //Console.WriteLine(a()); //Func<string, int> a = Test2;//func後面能夠跟不少類型,最後一個類型是返回值類型,前面的類型是參數類型,參數類型必須跟指向的方法的參數類型按照順序對應 Func<int, int, int> a = Test3;//func後面必須指定一個返回值類型,參數類型能夠有0-16個,先寫參數類型,最後一個是返回值類型 int res = a(1, 5); Console.WriteLine(res); Console.ReadKey(); } } }
前面使用的委託都只包含一個方法的調用,可是委託也能夠包含多個方法,這種委託叫作多播委託。使用多播委託就能夠按照順序調用多個方法,多播委託只能獲得調用的最後一個方法的結果,通常咱們把多播委託的返回類型聲明爲void。
Action action1 = Test1; action2+=Test2; action2-=Test1;
多播委託包含一個逐個調用的委託集合,若是經過委託調用的其中一個方法拋出異常,整個迭代就會中止。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _008_多播委託 { class Program { static void Test1() { Console.WriteLine("test1"); //throw new Exception(); } static void Test2() { Console.WriteLine("test2"); } static void Main(string[] args) { //多播委託 Action a = Test1; a += Test2;//表示添加一個委託的引用 //a -= Test1; //a -= Test2; //if(a!=null) // a();//當一個委託沒有指向任何方法的時候,調用的話會出現異常null // 取得多播委託中全部方法的委託 Delegate[] delegates = a.GetInvocationList(); foreach (Delegate de in delegates) { de.DynamicInvoke(); } Console.ReadKey(); } } }
到目前爲止,使用委託,都是先定義一個方法,而後把方法給委託的實例。但還有另一種使用委託的方式,不用去定義一個方法,應該說是使用匿名方法(方法沒有名字)。
Func<int,int,int> plus = delegate (int a,int b){ int temp = a+b; return temp; }; int res = plus(34,34); Console.WriteLine(res);
在這裏至關於直接把要引用的方法直接寫在了後面,優勢是減小了要編寫的代碼,減小代碼的複雜性
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _009_匿名方法 { class Program { static int Test1(int arg1, int arg2) { return arg1 + arg2; } static void Main(string[] args) { //Func<int, int, int> plus = Test1; //修改爲匿名方法的形式 Func<int, int, int> plus = delegate(int arg1, int arg2) { return arg1 + arg2; }; //匿名方法 本質上是一個方法,只是沒有名字,任何使用委託變量的地方均可以使用匿名方法賦值 Console.ReadKey(); } } }
從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語句。若是Lambda表達式的實現代碼中須要多條語句,就必須添加花括號和return語句。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _010_Lambda表達式 { class Program { static void Main(string[] args) { //lambda表達式用來代替匿名方法,因此一個lambda表達式也是定義了一個方法 //Func<int, int, int> plus = delegate(int arg1, int arg2) { // return arg1 + arg2; //}; //Func<int, int, int> plus = (arg1, arg2) =>// lambda表達式的參數是不須要聲明類型的 //{ // return arg1 + arg2; //}; //Console.WriteLine(plus(90,60)); Func<int, int> test2 = a => a+1;//lambda表示的參數只有一個的時候,能夠不加上括號 當函數體的語句只有一句的時候,咱們能夠不加上大括號 也能夠不加上return語句 Func<int, int> test3 = (a) => { return a + 1; }; Console.WriteLine(test2(34)); Console.WriteLine(test3(34)); Console.ReadKey(); } } }
經過Lambda表達式能夠訪問Lambda表達式塊外部的變量。這是一個很是好的功能,但若是不能正確使用,也會很是危險。示例:
int somVal = 5; Func<int,int> f = x=>x+somVal; Console.WriteLine(f(3));//8 somVal = 7; Console.WriteLine(f(3));//10
這個方法的結果,不但受到參數的控制,還受到somVal變量的控制,結果不可控,容易出現編程問題,用的時候要謹慎。
事件(event)基於委託,爲委託提供了一個發佈/訂閱機制,咱們能夠說事件是一種具備特殊簽名的委託。
好比:咱們按鍵點擊,這就是一個事件,在其它地方接收到這個事件,觸發相應。
下面使用貓和老鼠的實例來演示:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _012_觀察者設計模式_貓捉老鼠 { /// <summary> /// 貓類 /// </summary> class Cat { private string name; private string color; public Cat(string name, string color) { this.name = name; this.color = color; } /// <summary> /// 貓進屋(貓的狀態發生改變)(被觀察者的狀態發生改變) /// </summary> public void CatComing() { Console.WriteLine(color+"的貓"+name+"過來了,喵喵喵 ..."); if(catCome!=null) catCome(); } public event Action catCome;//聲明一個事件 發佈了一個消息 } }
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _012_觀察者設計模式_貓捉老鼠 { /// <summary> /// 觀察者類:老鼠 /// </summary> class Mouse { private string name; private string color; public Mouse(string name, string color,Cat cat) { this.name = name; this.color = color; cat.catCome += this.RunAway;//把自身的逃跑方法 註冊進 貓裏面 訂閱消息 } /// <summary> /// 逃跑功能 /// </summary> public void RunAway() { Console.WriteLine(color+"的老鼠"+name+"說: 老貓來, 趕忙跑, 我跑, 我使勁跑,我加速使勁跑 ..."); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _012_觀察者設計模式_貓捉老鼠 { class Program { static void Main(string[] args) { Cat cat = new Cat("加菲貓","黃色"); Mouse mouse1 = new Mouse("米奇","黑色",cat); Mouse mouse2 = new Mouse("水", "紅色",cat); cat.CatComing(); //cat.catCome();//事件不能再類的外部觸發,只能在類的內部觸發 Console.ReadKey(); } } }