溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C#中委托的基礎(chǔ)介紹與實現(xiàn)方法

發(fā)布時間:2021-08-03 15:18:57 來源:億速云 閱讀:102 作者:chen 欄目:開發(fā)技術(shù)

這篇文章主要講解了“C#中委托的基礎(chǔ)介紹與實現(xiàn)方法”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“C#中委托的基礎(chǔ)介紹與實現(xiàn)方法”吧!

目錄
  • 前言

  • 關(guān)于委托

  • 委托的實現(xiàn)

    • 一、基本實現(xiàn)方式

    • 二、使用委托時的一些特殊方式

      • 1、委托實例對象的創(chuàng)建多元化:

      • 2、事件綁定的多種方式

    • 三、委托的幾種特殊實現(xiàn)方式

      • 1,使用Action方法

        • 2,使用Func方法

      • 四、委托的一些特殊小知識

        • 1、委托閉包的產(chǎn)生

        • 2,關(guān)于事件


    前言

    似乎委托對于C#而言是一種高級屬性,但是我依舊希望你就算第一次看我的文章,也能有很大的收獲。

    所以本博客的語言描述盡量簡單易懂,知識點也是面向初入門對于委托不了解的學(xué)習(xí)者的。當(dāng)然如果有幸有大佬發(fā)現(xiàn)文章的錯誤點,也歡迎留言指出!

    關(guān)于委托

    關(guān)于委托的介紹主要來源于C#文檔:委托概述(本文章優(yōu)勢在于去掉一些不必要的細節(jié),對于初學(xué)者而言簡單高效)

    委托的定義主要是下面幾個方面:

    • 委托是一種引用類型:表示對具有特定參數(shù)列表和返回類型的方法的引用

    • 在實例化委托時,你可以將其實例與任何具有兼容簽名和返回類型的方法相關(guān)聯(lián)。 你可以通過委托實例調(diào)用方法

    委托本質(zhì)上來講就是將方法作為參數(shù)傳遞給其他方法的一種實現(xiàn)方式,當(dāng)然開發(fā)者也可以直接去調(diào)用方法。但是當(dāng)一個項目擴展到足夠大時,這種直接調(diào)用的方式就會很復(fù)雜,難以維護。而委托不會,可以很方便的進行后期的擴展開發(fā)。只需要將自己的方法傳入已經(jīng)寫好的對應(yīng)的委托即可。而不需要再在大量的代碼中找到調(diào)用處寫入自己的方法。

    關(guān)于委托的一些特點是(暫時不了解沒有關(guān)系):

    • 委托類似于 C++ 函數(shù)指針,但委托完全面向?qū)ο?,不?C++ 指針會記住函數(shù),委托會同時封裝對象實例和方法。

    • 委托允許將方法作為參數(shù)進行傳遞。

    • 委托可用于定義回調(diào)方法。

    • 委托可以鏈接在一起;例如,可以對一個事件調(diào)用多個方法。

    • 方法不必與委托類型完全匹配。 有關(guān)詳細信息,請參閱使用委托中的變體。

    • 使用Lambda 表達式可以更簡練地編寫內(nèi)聯(lián)代碼塊。 Lambda表達式(在某些上下文中)可編譯為委托類型。 若要詳細了解lambda 表達式,請參閱 lambda 表達式。

    如果對于一個初學(xué)者,你可以簡單的理解,委托就是一個更高級的調(diào)用方法的方式,而你要學(xué)習(xí)的,就是這種方式的實現(xiàn)方法,然后后期再慢慢理解更多的細節(jié)。

    委托的實現(xiàn)

    一、基本實現(xiàn)方式

    前面也說,委托是一個引用類型,要使用委托,肯定就需要對其進行定義并創(chuàng)建一個委托對象,下面使用一個帶有一個參數(shù)的案例來理解委托

    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);
            }    
        }

    如果你是剛剛接觸委托這個概念,可能對于這些代碼的含義不是特別了解,沒關(guān)系,你可以根據(jù)注釋的順序來看代碼并理解委托的實現(xiàn)機制:

    • T1:通過delegate關(guān)鍵字定義一個委托類型TestDel,并創(chuàng)建一個實例Del

    • T2: 也很好理解,創(chuàng)建兩個測試方法,可以執(zhí)行輸出

    • T3: 是委托的關(guān)鍵,為剛剛創(chuàng)建的委托來添加事件方法

    • T4: 執(zhí)行委托實例Del,可以簡單理解為執(zhí)行該實例綁定的所有事件方法

    當(dāng)然有一些語法是比較獨特的,比如說+=這樣的語法,就是一種為委托添加事件方法的方法,而對于執(zhí)行委托語句demo.Del?.Invoke("你們好");中Invoke()為執(zhí)行該委托對象內(nèi)方法的API,?則可以在Del委托對象為空時,系統(tǒng)不報錯

    關(guān)于?的具體含義,可查閱:

    可為空引用類型

    其實委托主要是有這簡單的四步來實現(xiàn)了,通過這個案例,更加明顯的體現(xiàn)出委托將一系列方法作為參數(shù)來讓其他方法去調(diào)用的特點。

    二、使用委托時的一些特殊方式

    通過上面的案例,可以看出對于委托的實現(xiàn)是很簡單的,但是C#還是為我們提供了很多更加間接或者集成的用法,具體有:

    1、委托實例對象的創(chuàng)建多元化:

    創(chuàng)建委托類型的多種方式:

    • 直接使用New來創(chuàng)建一個對象,但是注意,在New時需要綁定直接添加一個事件方法,不然會報錯(這也是與其他引用類型不同的地方)

    • 使用直接賦值的方式創(chuàng)建

    • 定義方法名,后期添加事件方法時自動實例(上面的例子)

    關(guān)于具體的實現(xiàn)代碼:

    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);
        }
    }

    注意,關(guān)于第三種后期綁定有一種現(xiàn)象值得留意,在使用后期綁定時,如果你是創(chuàng)建一個成員變量(全局變量)委托類型,后期綁定可以直接使用+=來增加委托綁定的方法,而你如果是創(chuàng)建一個局部變量的委托,需要先通過=來添加一個方法后,才能使用+=來增加方法,不然就會報空,如圖:

    C#中委托的基礎(chǔ)介紹與實現(xiàn)方法

    出現(xiàn)這種情況的原因在于成員變量與局部變量之間的區(qū)別,如果想要了解更多,可以執(zhí)行百度,這邊列出兩者在本案例中的區(qū)別:

    成員變量:有默認初始化值局部變量:沒有默認初始化值,必須定義,賦值,然后才能使用。

    2、事件綁定的多種方式

    對于事件的綁定,可以使用的方式有很多,在不同的情況下不同的方式也有不同的優(yōu)勢與局限性,可以根據(jù)自己的需求進行自行選擇

    • 使用方法名通過+=來添加方法,可以通過-=來刪除方法

    • 使用匿名方法

    • 使用Lambda表達式

    關(guān)于第一種方法,已經(jīng)在上面表示的很清楚。

    關(guān)于社會的進步與發(fā)展,某一方面來講,是由于人類的懶來驅(qū)動的,C#開發(fā)人員可能覺得第一種方式太復(fù)雜了,于是就出現(xiàn)匿名函數(shù)的腳本,先通過代碼來看一下其實現(xiàn)方式:

    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("這是一個匿名方法的測試");
            };
            
        }
    }

    通過上面的代碼可以看出,通過匿名方式使得我們不需要重新定義方法來進行綁定,只需要通過委托關(guān)鍵字,而省去數(shù)據(jù)類型修飾符、方法簽名等結(jié)構(gòu)

    匿名方法定義(菜鳥教程):

    匿名方法提供了一種傳遞代碼塊作為委托參數(shù)的技術(shù)。匿名方法是沒有名稱只有主體的方法。在匿名方法中不需要指定返回類型,它是從方法主體內(nèi)的 return 語句推斷的()

    而Lambda 表達式更加極致,將能省掉的東西全部省掉,使得最終的表達式極其的簡潔:

    public class DemoDelegate
        {
            delegate void TestDel(string str);
            TestDel Del;
            static void Main(string[] args)
            {
                DemoDelegate demo = new DemoDelegate();
           		//lambda表達式,括號內(nèi)為參數(shù)變量名,如果沒有直接()
                demo.Del += (str)=>
                {
                    Console.WriteLine(str);
                };
           
            }
        }

    可以通過腳本看出,Lambda 表達式對于語法的節(jié)省到達了極致,去掉了所有的修飾符,包括傳入的參數(shù)的數(shù)據(jù)類型修飾符,只需要一個變量名即可,而一個委托如果沒有參數(shù),直接使用()即可,簡單到極致

    注意(關(guān)于()的應(yīng)用):

    如果Lambda 無參數(shù),則必須要有()
    有一個參數(shù)可以去掉(),直接+= str=>{};
    如果有多個參數(shù),也必須要有()

    三、委托的幾種特殊實現(xiàn)方式

    除了使用delegate關(guān)鍵字來實現(xiàn)委托外,C#還提供了幾種升級版的集成化的使用方式,比如Action和Func方法等等。但是注意,高的集成化往往意味著低的適配性。所以對于下面介紹幾種方式他們往往有一定的使用限制,不如delegate來的靈活,不過對于特定場景更加的簡單快捷

    1,使用Action方法

    在開始介紹使用方式之前,先說明一下其使用特殊

    • 對于沒有返回值的委托,簡單的理解就是不需要return(這種想法是錯誤的,但是好理解)

    其實很簡單的對不對,其腳本實現(xiàn)更加簡單:

    //無參數(shù)的Action委托對象的定義
    Action<> actDemoOne;
    
    //帶參數(shù)的Action委托對象的定義,參數(shù)最多十六個
    Action<int> actDemoTwo;

    除了定義不同外,創(chuàng)建的委托實例對于方法的綁定與執(zhí)行與標(biāo)準(zhǔn)的委托相同,其實Action本來就是通過delegate來定義的一個委托類型,但是這個定義是C#系統(tǒng)進行定義的。

    在代碼中我們很容易找到這句定義

    C#中委托的基礎(chǔ)介紹與實現(xiàn)方法

    而帶參數(shù)的類型與上面類似

    C#中委托的基礎(chǔ)介紹與實現(xiàn)方法

    這樣我們可以直接使用C#提供定義好的委托類型來創(chuàng)建我們的委托實例,這種方式的優(yōu)勢就是代碼結(jié)構(gòu)簡潔好用。不過限制就是只能綁定沒有返回值的方法

    2,使用Func方法

    其實Func的用法是與Action相反互補的,其主要的特點是有返回值,為了突出,依舊列出來

    • 主要用于沒有參數(shù)的委托類型,且必須有返回值

    • 但是同時是可以有參數(shù)的,并不是與Action完全相反

    通過代碼來表述這一特點:

    // int為該委托綁定方法的返回值類型,注意必須要有返回值
    Func<int> funDemo;
    
    //綁定與執(zhí)行與標(biāo)準(zhǔn)委托相同,來復(fù)習(xí)一下
    public class DemoDelegate
    {
            static void Main(string[] args)
            {
                 DemoDelegate demo = new DemoDelegate();
                
                Func<int> funDemo;
                //為委托添加方法
                funDemo = demo.TestEventOne;
                //執(zhí)行委托
                funDemo?.Invoke();
                //依舊可以使用Lambda表達式
                funDemo += () =>
                {
    
                    return 0;
                };
                
                /*-----------帶參數(shù)的Func用法--------*/
                Func<string,int> funDemoTwo;
                funDemoTwo = demo.TestEventTwo;            
            }
        
            public int TestEventOne()
            {
                return 0;
            }
        
        	public int TestEventTwo(string str)
            {
                Console.WriteLine(str);
                return 0;
            }       
    }

    上面的代碼將兩種情況放在一起解釋的,可以分開邏輯進行理解,但是重要的是要理解帶參數(shù)的Func的用法,我們可以看到Func<string,int>有兩個數(shù)據(jù)類型的寫入,前面一個就是傳入?yún)?shù)的類型,而后面就是返回值類型,一定要區(qū)分開來

    四、委托的一些特殊小知識

    1、委托閉包的產(chǎn)生

    在正式開始介紹閉包概念之前,先通過一個案例來發(fā)掘關(guān)于閉包產(chǎn)生的現(xiàn)象,你可以猜想一下下面的代碼的輸出結(jié)果:

    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();
        	}
        }

    如果根據(jù)正常的邏輯思路去判斷,會很直接的判斷得到的輸出為:0、1、2,但是經(jīng)過執(zhí)行打印卻發(fā)現(xiàn)最終的輸出結(jié)果為:3、3、3(為啥不是2、2、2,思考一下++i與i++,或者往后看)

    這樣的結(jié)果確實很奇妙,但是如果認真思考,你可能會有一些小想法,是不是由于存儲的是數(shù)據(jù)i地址呢,而導(dǎo)致最終結(jié)果相同呢。其實可以簡單的理解成這個樣子。

    如果想要更加深入的了解閉包,希望下面的一些解釋可以幫助你理解

    閉包概念:

    是一個函數(shù)與其他相關(guān)引用環(huán)境組合的實體,而在引用環(huán)境消失后,該函數(shù)會依舊保存從函數(shù)內(nèi)引用的變量

    根據(jù)上面的例子來翻譯一下就是委托的代碼塊使用了代碼塊外的變量,而這個變量是Test()方法的局部變量,當(dāng)Test()方法執(zhí)行完畢后,這個局部變量本應(yīng)該被銷毀,但是由于閉包原因卻保存其狀態(tài)到內(nèi)存中,來保證自己后續(xù)的使用。

    因此,所有的委托方法需要的變量內(nèi)存地址都指向了本應(yīng)被銷毀的局部變量i,最終的讀取就是i最后的狀態(tài),也就是全是經(jīng)過三次i++后的3

    閉包需要注意的問題

    如果使用必要,需要注意由于閉包而產(chǎn)生的內(nèi)存泄露的問題,由于閉包是訪問另一個函數(shù)中的變量,就會影響另一函數(shù)中的局部變量的內(nèi)存回收。而一直占用在內(nèi)存中。造成內(nèi)存泄露的后果。

    但是也有另外的觀點講閉包不會造成內(nèi)存泄露。原因在于C#的垃圾回收是有相應(yīng)的處理的。由于本人對于垃圾回收機制認識比較淺顯,目前也做不出判斷。還希望了解的人可以留言告知

    2,關(guān)于事件

    在C#中,也提出了事件這個概念,本質(zhì)上來講也是委托,但是是一個受限的委托

    與靈活的委托不同,事件只能在定義的類內(nèi)被調(diào)用,不過可以在其他類里面進行方法的綁定

    事件的用法

    在我們定義一個委托類型后,我們可以通過event創(chuàng)建一個事件實例:

    C#中委托的基礎(chǔ)介紹與實現(xiàn)方法

    通過上面的案例可以看到,使用event修飾委托實例后,只能夠在定義類中執(zhí)行委托的方法,而不能在其他類中去調(diào)用執(zhí)行

    感謝各位的閱讀,以上就是“C#中委托的基礎(chǔ)介紹與實現(xiàn)方法”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對C#中委托的基礎(chǔ)介紹與實現(xiàn)方法這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

    向AI問一下細節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI