香蕉视频app

香蕉视频appKeep on going never give up.

Let's Go

C# 學習筆記(50)委托

C#Lonely2019-05-25 23:22:52137次0條

C#中的委托可以理解為函數的包裝,它使得C#中的函數可以被作為參數來被傳遞。這種將函數動態地賦給參數的做法,可以避免在程序中大量使用if-else(Switch)語句,同時使得程序具有更好的可擴展性。用最通俗易懂的話來講,你就可以把委托看成是用來執行方法(函數)的一個東西。在使用委托的時候,你可以像對待一個類一樣對待它。即先聲明,再實例化。只是有點不同,類在實例化之后叫對象或實例,但委托在實例化后仍叫委托。

委托的定義和方法的定義類似,只是在定義的前面多了一個delegate關鍵字。

可以被委托包裝的方法必須滿足以下規則:

1、方法的簽名必須與委托一致,方法簽名包括參數的個數、類型和順序。

2、方法的返回類型要和委托一致,注意,方法的返回類型不屬于方法簽名的一部分。

香蕉视频app 委托使得一個方法可以作為另一個方法的參數進行傳遞,這就是委托最大的作用。

香蕉视频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);
        }

    }
}

運行結果:

image.png


委托的本質

委托是類類型,委托的本質就是一個類。通過上面的代碼,反編譯查看其生成的IL代碼,如下圖所示:

image.png

由上圖可知,委托類型使用.class關鍵字進行標識,實際上定義任何委托編譯器都會做如下工作:

1、聲明一個類,對應上圖中的.class nested public auto ansi sealed。

香蕉视频app 2、該類擴展自System.MulticastDelegate,對應上圖中的extends [mscorlib]System.MulticastDelegate。

3、該類包含一個構造器,對應上圖中的.ctor: void(object ,native int)。

4、該類包含三個方法,分別是BeginInvoke、EndInvoke、Invoke。

任何委托都繼承自[mscorlib]System.MulticastDelegate類型,該類包含了一個構造函數和3個方法,有了構造函數,我們才能使用new關鍵字來實例化委托類型。而Inoke方法則用來顯式地調用委托,此外IAsyncResult和EndInvoke方法是兩個異步方法。


多播委托

C#中把封裝多個方法的委托稱作為委托鏈多路廣播委托

香蕉视频app 委托鏈其實就是委托類型,只是委托鏈把多個委托鏈接在一起而已,也就是說,我們把鏈接了多個方法的委托稱為委托鏈或多路廣播委托。

代碼示例:

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

    }
}

香蕉视频app 運行結果:

image.png

香蕉视频app 從以上代碼可知,通過使用“+”運算符,我們能將多個委托鏈接到一個委托對象實例上,使其成為多路廣播委托。在調用委托鏈時,被綁定到委托鏈中的每個委托都會被執行。

從委托鏈中移除委托:既然能用“+”運算符把委托鏈接到一個委托對象實例上,自然也可以使用“-”運算符將某個委托從委托鏈對象上移除。


泛型委托

泛型委托,與普通委托類似,不同之處只在于使用泛型委托要指定泛型參數,為了方便開發,.NET基類庫針對在實際開發中最常用的情形提供了幾個預定義好的委托,以免我們在自己使用時還繁瑣重復的去定義它,它們分別是ActionFuncPredicate,這是我在資料上摘取的這幾個委托的區別:

香蕉视频app (1)、Action

香蕉视频app Action 是無返回值的泛型委托。

香蕉视频app Action 表示無參,無返回值的委托

Action<int,string> 表示有傳入參數int,string無返回值的委托

Action<int,string,bool> 表示有傳入參數int,string,bool無返回值的委托

Action<int,int,int,int> 表示有傳入4個int型參數,無返回值的委托

Action至少0個參數,至多16個參數,無返回值。

(2)、Func

香蕉视频app Func是有返回值的泛型委托

Func<int> 表示無參,返回值為int的委托

香蕉视频app Func<object,string,int> 表示傳入參數為object,string 返回值為int的委托

香蕉视频app Func<object,string,int> 表示傳入參數為object, string 返回值為int的委托

香蕉视频app Func<T1,T2,T3,int> 表示傳入參數為T1,T2,T3(泛型)返回值為int的委托

香蕉视频app Func至少0個參數,至多16個參數,根據返回值泛型返回。必須有返回值,不可void

(3)、Predicate

predicate 是返回bool型的泛型委托

predicate<int> 表示傳入參數為int 返回bool的委托

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

    }
}

運行結果:

image.png






暗錨,解決錨點偏移

文章評論

    嘿,來試試登錄吧!