C#中的委托可以理解為函數的包裝,它使得C#中的函數可以被作為參數來被傳遞。這種將函數動態地賦給參數的做法,可以避免在程序中大量使用if-else(Switch)語句,同時使得程序具有更好的可擴展性。用最通俗易懂的話來講,你就可以把委托看成是用來執行方法(函數)的一個東西。在使用委托的時候,你可以像對待一個類一樣對待它。即先聲明,再實例化。只是有點不同,類在實例化之后叫對象或實例,但委托在實例化后仍叫委托。
委托的定義和方法的定義類似,只是在定義的前面多了一個delegate關鍵字。
香蕉视频app 可以被委托包裝的方法必須滿足以下規則:
1、方法的簽名必須與委托一致,方法簽名包括參數的個數、類型和順序。
2、方法的返回類型要和委托一致,注意,方法的返回類型不屬于方法簽名的一部分。
委托使得一個方法可以作為另一個方法的參數進行傳遞,這就是委托最大的作用。
香蕉视频app 代碼示例:
using System; namespace ConsoleApp { public delegate void Greeting(string name); //定義委托類型 class Program { static void Main(string[] args) { //聲明委托變量,并實例化委托類型 Greeting greeting_1 = new Greeting(ChineseGreeting); //上面這句,可以簡寫為下面的形式,其本質上還是new Greeting,只不過這部分編譯器幫我們自動完成了 Greeting greeting_2 = EnglishGreeting; //調用委托 greeting_1("林莉"); //隱式調用委托,其背后仍通過調用Invoke方法來實現的,只不過這一步由編譯器來幫我們完成了。 greeting_2.Invoke("沈婷婷"); //顯式調用委托 //匿名函數 Greeting greeting_3 = delegate (string name) { Console.WriteLine("你好"+name+ ",我是吳亦凡,你看這燈,多亮。"); }; greeting_3("李雪琴"); // lamda表達式,其本質也是匿名函數 => “goes to” Greeting greeting_4 = (string name) => { Console.WriteLine("你好"+name+ ",雞你真美。"); }; greeting_4("蔡徐坤"); Console.ReadKey(); } //英文問候 public static void EnglishGreeting(string name) { Console.WriteLine("Hello,"+name); } //中文問候 public static void ChineseGreeting(string name) { Console.WriteLine("你好," + name); } } }
運行結果:
委托的本質
委托是類類型,委托的本質就是一個類。通過上面的代碼,反編譯查看其生成的IL代碼,如下圖所示:
香蕉视频app 由上圖可知,委托類型使用.class關鍵字進行標識,實際上定義任何委托編譯器都會做如下工作:
1、聲明一個類,對應上圖中的.class nested public auto ansi sealed。
2、該類擴展自System.MulticastDelegate,對應上圖中的extends [mscorlib]System.MulticastDelegate。
香蕉视频app 3、該類包含一個構造器,對應上圖中的.ctor: void(object ,native int)。
香蕉视频app 4、該類包含三個方法,分別是BeginInvoke、EndInvoke、Invoke。
任何委托都繼承自[mscorlib]System.MulticastDelegate類型,該類包含了一個構造函數和3個方法,有了構造函數,我們才能使用new關鍵字來實例化委托類型。而Inoke方法則用來顯式地調用委托,此外IAsyncResult和EndInvoke方法是兩個異步方法。
多播委托
C#中把封裝多個方法的委托稱作為委托鏈或多路廣播委托。
委托鏈其實就是委托類型,只是委托鏈把多個委托鏈接在一起而已,也就是說,我們把鏈接了多個方法的委托稱為委托鏈或多路廣播委托。
代碼示例:
using System; namespace ConsoleApp { public delegate void Greeting(string name); //定義委托類型 class Program { static void Main(string[] args) { Greeting greeting = ChineseGreeting; greeting += EnglishGreeting; //使用“+”符號鏈接委托,鏈接多個委托后就成為了委托鏈 greeting("唐麗麗"); Console.ReadKey(); } //英文問候 public static void EnglishGreeting(string name) { Console.WriteLine("Hello,"+name); } //中文問候 public static void ChineseGreeting(string name) { Console.WriteLine("你好," + name); } } }
運行結果:
從以上代碼可知,通過使用“+”運算符,我們能將多個委托鏈接到一個委托對象實例上,使其成為多路廣播委托。在調用委托鏈時,被綁定到委托鏈中的每個委托都會被執行。
從委托鏈中移除委托:既然能用“+”運算符把委托鏈接到一個委托對象實例上,自然也可以使用“-”運算符將某個委托從委托鏈對象上移除。
泛型委托
泛型委托,與普通委托類似,不同之處只在于使用泛型委托要指定泛型參數,為了方便開發,.NET基類庫針對在實際開發中最常用的情形提供了幾個預定義好的委托,以免我們在自己使用時還繁瑣重復的去定義它,它們分別是Action、Func和Predicate,這是我在資料上摘取的這幾個委托的區別:
香蕉视频app (1)、Action
Action 是無返回值的泛型委托。
香蕉视频app Action 表示無參,無返回值的委托
香蕉视频app Action<int,string> 表示有傳入參數int,string無返回值的委托
Action<int,string,bool> 表示有傳入參數int,string,bool無返回值的委托
香蕉视频app Action<int,int,int,int> 表示有傳入4個int型參數,無返回值的委托
香蕉视频app Action至少0個參數,至多16個參數,無返回值。
(2)、Func
香蕉视频app Func是有返回值的泛型委托
Func<int> 表示無參,返回值為int的委托
香蕉视频app Func<object,string,int> 表示傳入參數為object,string 返回值為int的委托
Func<object,string,int> 表示傳入參數為object, string 返回值為int的委托
香蕉视频app Func<T1,T2,T3,int> 表示傳入參數為T1,T2,T3(泛型)返回值為int的委托
Func至少0個參數,至多16個參數,根據返回值泛型返回。必須有返回值,不可void
(3)、Predicate
predicate 是返回bool型的泛型委托
predicate<int> 表示傳入參數為int 返回bool的委托
香蕉视频app Predicate有且只有一個參數,返回值固定為bool
一般的需求下,我們就使用微軟定義的委托就足夠了,這樣減少了我們對委托的重復定義,可能有部分初學者見到Func<>,Action<>這樣的代碼肯定會很懵比,這只是你對新東西陌生罷了,多結合實例敲幾遍,自然就會用了,它們其實就是微軟封裝定義好了的委托,沒有什么特別的。
代碼示例:
using System; namespace ConsoleApp { class Program { static void Main(string[] args) { //Action<T> 用法,比起上面的自定義委托,明顯可以看出代碼簡潔了 Action<string> greeting = ChineseGreeting; greeting("沈月"); Console.ReadKey(); } //英文問候 public static void EnglishGreeting(string name) { Console.WriteLine("Hello,"+name); } //中文問候 public static void ChineseGreeting(string name) { Console.WriteLine("你好," + name); } } }
使用泛型委托,求任意類型數組最大值,代碼示例:
using System; namespace ConsoleApp { public delegate int DelCompare<T>(T t1,T t2); //定義泛型委托 class Program { static void Main(string[] args) { int[] nums = { 10,20,30,40,50 }; int max = GetMax<int>(nums, Compare); Console.WriteLine("該數組最大值是:"+max); string[] arr = { "我將于茫茫人海中訪我唯一靈魂之伴侶", "得之", "我幸", "不得", "我命", "徐志摩" }; string strMaxLength = GetMax<string>(arr, (string s1, string s2) => { return s1.Length - s2.Length; }); Console.WriteLine("最大長度的字符串是:"+strMaxLength); Console.ReadKey(); } public static T GetMax<T>(T[] nums, DelCompare<T> del) { T max = nums[0]; for (int i = 0; i < nums.Length; i++) { //要傳一個比較方法 if (del(max, nums[i]) < 0) max = nums[i]; } return max; } // int數組比較方法 public static int Compare(int n1, int n2) { return n1 - n2; } } }
運行結果:
文章評論