您好,登錄后才能下訂單哦!
這篇文章主要講解了“C#中委托的基礎介紹與實現方法”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“C#中委托的基礎介紹與實現方法”吧!
前言
關于委托
委托的實現
一、基本實現方式
二、使用委托時的一些特殊方式
1、委托實例對象的創建多元化:
2、事件綁定的多種方式
三、委托的幾種特殊實現方式
1,使用Action方法
2,使用Func方法
四、委托的一些特殊小知識
1、委托閉包的產生
2,關于事件
似乎委托對于C#而言是一種高級屬性,但是我依舊希望你就算第一次看我的文章,也能有很大的收獲。
所以本博客的語言描述盡量簡單易懂,知識點也是面向初入門對于委托不了解的學習者的。當然如果有幸有大佬發現文章的錯誤點,也歡迎留言指出!
關于委托的介紹主要來源于C#文檔:委托概述(本文章優勢在于去掉一些不必要的細節,對于初學者而言簡單高效)
委托的定義主要是下面幾個方面:
委托是一種引用類型:表示對具有特定參數列表和返回類型的方法的引用
在實例化委托時,你可以將其實例與任何具有兼容簽名和返回類型的方法相關聯。 你可以通過委托實例調用方法
委托本質上來講就是將方法作為參數傳遞給其他方法的一種實現方式,當然開發者也可以直接去調用方法。但是當一個項目擴展到足夠大時,這種直接調用的方式就會很復雜,難以維護。而委托不會,可以很方便的進行后期的擴展開發。只需要將自己的方法傳入已經寫好的對應的委托即可。而不需要再在大量的代碼中找到調用處寫入自己的方法。
關于委托的一些特點是(暫時不了解沒有關系):
委托類似于 C++ 函數指針,但委托完全面向對象,不像 C++ 指針會記住函數,委托會同時封裝對象實例和方法。
委托允許將方法作為參數進行傳遞。
委托可用于定義回調方法。
委托可以鏈接在一起;例如,可以對一個事件調用多個方法。
方法不必與委托類型完全匹配。 有關詳細信息,請參閱使用委托中的變體。
使用Lambda 表達式可以更簡練地編寫內聯代碼塊。 Lambda表達式(在某些上下文中)可編譯為委托類型。 若要詳細了解lambda 表達式,請參閱 lambda 表達式。
如果對于一個初學者,你可以簡單的理解,委托就是一個更高級的調用方法的方式,而你要學習的,就是這種方式的實現方法,然后后期再慢慢理解更多的細節。
前面也說,委托是一個引用類型,要使用委托,肯定就需要對其進行定義并創建一個委托對象,下面使用一個帶有一個參數的案例來理解委托
public class DemoDelegate { //T1: delegate void TestDel(string s); TestDel Del; //T4: static void Main(string[] args) { DemoDelegate demo = new DemoDelegate(); demo.CreateDelObject(); demo.Del?.Invoke("你們好"); } //T3: public void CreateDelObject() { Del += TestEventOne; Del += TestEventTwo; } //T2: public void TestEventOne(string str) { Console.WriteLine(str); } public void TestEventTwo(string str) { Console.WriteLine(str); } }
如果你是剛剛接觸委托這個概念,可能對于這些代碼的含義不是特別了解,沒關系,你可以根據注釋的順序來看代碼并理解委托的實現機制:
T1:通過delegate關鍵字定義一個委托類型TestDel,并創建一個實例Del
T2: 也很好理解,創建兩個測試方法,可以執行輸出
T3: 是委托的關鍵,為剛剛創建的委托來添加事件方法
T4: 執行委托實例Del,可以簡單理解為執行該實例綁定的所有事件方法
當然有一些語法是比較獨特的,比如說+=這樣的語法,就是一種為委托添加事件方法的方法,而對于執行委托語句demo.Del?.Invoke("你們好");
中Invoke()為執行該委托對象內方法的API,?則可以在Del委托對象為空時,系統不報錯
關于?的具體含義,可查閱:
可為空引用類型
其實委托主要是有這簡單的四步來實現了,通過這個案例,更加明顯的體現出委托將一系列方法作為參數來讓其他方法去調用的特點。
通過上面的案例,可以看出對于委托的實現是很簡單的,但是C#還是為我們提供了很多更加間接或者集成的用法,具體有:
創建委托類型的多種方式:
直接使用New來創建一個對象,但是注意,在New時需要綁定直接添加一個事件方法,不然會報錯(這也是與其他引用類型不同的地方)
使用直接賦值的方式創建
定義方法名,后期添加事件方法時自動實例(上面的例子)
關于具體的實現代碼:
public class DemoDelegate { delegate void TestDel(string str); //第一種:New 的同時綁定方法 TestDel DelOne = new TestDel(TestEventOne); //第二種 TestDel DelTwo = TestEventOne; static void TestEventOne(string str) { Console.WriteLine(str); } }
注意,關于第三種后期綁定有一種現象值得留意,在使用后期綁定時,如果你是創建一個成員變量(全局變量)委托類型,后期綁定可以直接使用+=來增加委托綁定的方法,而你如果是創建一個局部變量的委托,需要先通過=
來添加一個方法后,才能使用+=來增加方法,不然就會報空,如圖:
出現這種情況的原因在于成員變量與局部變量之間的區別,如果想要了解更多,可以執行百度,這邊列出兩者在本案例中的區別:
成員變量:有默認初始化值局部變量:沒有默認初始化值,必須定義,賦值,然后才能使用。
對于事件的綁定,可以使用的方式有很多,在不同的情況下不同的方式也有不同的優勢與局限性,可以根據自己的需求進行自行選擇
使用方法名通過+=來添加方法,可以通過-=來刪除方法
使用匿名方法
使用Lambda表達式
關于第一種方法,已經在上面表示的很清楚。
關于社會的進步與發展,某一方面來講,是由于人類的懶來驅動的,C#開發人員可能覺得第一種方式太復雜了,于是就出現匿名函數的腳本,先通過代碼來看一下其實現方式:
public class DemoDelegate { delegate void TestDel(string str); TestDel Del; static void Main(string[] args) { DemoDelegate demo = new DemoDelegate(); //匿名方法使用方式演示 demo.Del = delegate (string str) { Console.WriteLine("這是一個匿名方法的測試"); }; } }
通過上面的代碼可以看出,通過匿名方式使得我們不需要重新定義方法來進行綁定,只需要通過委托關鍵字,而省去數據類型修飾符、方法簽名等結構
匿名方法定義(菜鳥教程):
匿名方法提供了一種傳遞代碼塊作為委托參數的技術。匿名方法是沒有名稱只有主體的方法。在匿名方法中不需要指定返回類型,它是從方法主體內的 return 語句推斷的()
而Lambda 表達式更加極致,將能省掉的東西全部省掉,使得最終的表達式極其的簡潔:
public class DemoDelegate { delegate void TestDel(string str); TestDel Del; static void Main(string[] args) { DemoDelegate demo = new DemoDelegate(); //lambda表達式,括號內為參數變量名,如果沒有直接() demo.Del += (str)=> { Console.WriteLine(str); }; } }
可以通過腳本看出,Lambda 表達式對于語法的節省到達了極致,去掉了所有的修飾符,包括傳入的參數的數據類型修飾符,只需要一個變量名即可,而一個委托如果沒有參數,直接使用()即可,簡單到極致
注意(關于()的應用):
如果Lambda 無參數,則必須要有()
有一個參數可以去掉(),直接+= str=>{};
如果有多個參數,也必須要有()
除了使用delegate關鍵字來實現委托外,C#還提供了幾種升級版的集成化的使用方式,比如Action和Func方法等等。但是注意,高的集成化往往意味著低的適配性。所以對于下面介紹幾種方式他們往往有一定的使用限制,不如delegate來的靈活,不過對于特定場景更加的簡單快捷
在開始介紹使用方式之前,先說明一下其使用特殊
對于沒有返回值的委托,簡單的理解就是不需要return(這種想法是錯誤的,但是好理解)
其實很簡單的對不對,其腳本實現更加簡單:
//無參數的Action委托對象的定義 Action<> actDemoOne; //帶參數的Action委托對象的定義,參數最多十六個 Action<int> actDemoTwo;
除了定義不同外,創建的委托實例對于方法的綁定與執行與標準的委托相同,其實Action本來就是通過delegate來定義的一個委托類型,但是這個定義是C#系統進行定義的。
在代碼中我們很容易找到這句定義
而帶參數的類型與上面類似
這樣我們可以直接使用C#提供定義好的委托類型來創建我們的委托實例,這種方式的優勢就是代碼結構簡潔好用。不過限制就是只能綁定沒有返回值的方法
其實Func的用法是與Action相反互補的,其主要的特點是有返回值,為了突出,依舊列出來
主要用于沒有參數的委托類型,且必須有返回值
但是同時是可以有參數的,并不是與Action完全相反
通過代碼來表述這一特點:
// int為該委托綁定方法的返回值類型,注意必須要有返回值 Func<int> funDemo; //綁定與執行與標準委托相同,來復習一下 public class DemoDelegate { static void Main(string[] args) { DemoDelegate demo = new DemoDelegate(); Func<int> funDemo; //為委托添加方法 funDemo = demo.TestEventOne; //執行委托 funDemo?.Invoke(); //依舊可以使用Lambda表達式 funDemo += () => { return 0; }; /*-----------帶參數的Func用法--------*/ Func<string,int> funDemoTwo; funDemoTwo = demo.TestEventTwo; } public int TestEventOne() { return 0; } public int TestEventTwo(string str) { Console.WriteLine(str); return 0; } }
上面的代碼將兩種情況放在一起解釋的,可以分開邏輯進行理解,但是重要的是要理解帶參數的Func的用法,我們可以看到Func<string,int>
有兩個數據類型的寫入,前面一個就是傳入參數的類型,而后面就是返回值類型,一定要區分開來
在正式開始介紹閉包概念之前,先通過一個案例來發掘關于閉包產生的現象,你可以猜想一下下面的代碼的輸出結果:
class DemoDelegate { delegate void TestDel(); TestDel Del; static void Main(string[] args) { DemoDelegate demo = new DemoDelegate(); for (int i = 0; i < 3; i++) { demo.Del+= () => { Console.WriteLine(i); }; } demo.Del?.Invoke(); } }
如果根據正常的邏輯思路去判斷,會很直接的判斷得到的輸出為:0、1、2,但是經過執行打印卻發現最終的輸出結果為:3、3、3(為啥不是2、2、2,思考一下++i與i++,或者往后看)
這樣的結果確實很奇妙,但是如果認真思考,你可能會有一些小想法,是不是由于存儲的是數據i地址呢,而導致最終結果相同呢。其實可以簡單的理解成這個樣子。
如果想要更加深入的了解閉包,希望下面的一些解釋可以幫助你理解
閉包概念:
是一個函數與其他相關引用環境組合的實體,而在引用環境消失后,該函數會依舊保存從函數內引用的變量
根據上面的例子來翻譯一下就是委托的代碼塊使用了代碼塊外的變量,而這個變量是Test()方法的局部變量,當Test()方法執行完畢后,這個局部變量本應該被銷毀,但是由于閉包原因卻保存其狀態到內存中,來保證自己后續的使用。
因此,所有的委托方法需要的變量內存地址都指向了本應被銷毀的局部變量i,最終的讀取就是i最后的狀態,也就是全是經過三次i++后的3
閉包需要注意的問題
如果使用必要,需要注意由于閉包而產生的內存泄露的問題,由于閉包是訪問另一個函數中的變量,就會影響另一函數中的局部變量的內存回收。而一直占用在內存中。造成內存泄露的后果。
但是也有另外的觀點講閉包不會造成內存泄露。原因在于C#的垃圾回收是有相應的處理的。由于本人對于垃圾回收機制認識比較淺顯,目前也做不出判斷。還希望了解的人可以留言告知
在C#中,也提出了事件這個概念,本質上來講也是委托,但是是一個受限的委托
與靈活的委托不同,事件只能在定義的類內被調用,不過可以在其他類里面進行方法的綁定
事件的用法
在我們定義一個委托類型后,我們可以通過event創建一個事件實例:
通過上面的案例可以看到,使用event修飾委托實例后,只能夠在定義類中執行委托的方法,而不能在其他類中去調用執行
感謝各位的閱讀,以上就是“C#中委托的基礎介紹與實現方法”的內容了,經過本文的學習后,相信大家對C#中委托的基礎介紹與實現方法這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。