iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 8
0
自我挑戰組

C# 從入門到WebApi系列 第 8

[Day8] 委派(delegate),Action<T> ,與Func<T,TResult>

  • 分享至 

  • xImage
  •  

什麼是委派

在一開始接觸到這個名詞時
我也是一頭霧水
讓我們來看看官方文件怎麼寫

委派是一種類型,代表具有特定參數清單及傳回型別的方法參考。 當您具現化委派時,可以將其執行個體與任何具有>>相容簽章和傳回型別的方法產生關聯。 您可以透過委派執行個體叫用 (或呼叫) 方法。

看完之後有沒有比較了解委派在幹嘛(我那時候直接把滑鼠移到右上角的XX按下去)

以我的話解釋 委派其實是代替你執行某種方法(就是你要執行的那個,但你現在不知道要執行的是哪個),
事實上委派很像函式指標,裡面存著你要執行方法的記憶體位置
把你的游標從XX移開Orz
我們直接先來看看範例吧(我覺得從範例比較好懂)

宣告一個委派

我們在昨天的[Day7] 豪想要一種能接納所有人的型別 關於多載與泛型 文章中有提到方法簽章這個東西
再我們宣告委派的時候同樣有方法簽章
它是一樣是由傳入類型與回傳類型所決定

注意

在方法多載的內容中,方法的簽章並不包括傳回值。 不過在委派的內容中,簽章卻包含傳回值。 換句話說,方法必須與委派擁有相同的傳回類型。

我們先來建立一個委派 使用delegate 關鍵字:
public delegate 回傳類型 委派名稱(傳入參數);

跟方法不同沒有你的程式碼喔
因為委派是執行你的方法 不用再自定義了不然就直接用方法就好了

public delegate void PrintNumber(int num);

input為int output為void(不回傳)

然後我們來宣告2個方法 跟我們委派一樣的方法簽章
(input為int output為void)

public static void PrintOddNumber(int num){
    Console.WriteLine("輸入的是奇數:"+num);
}
public static void PrintEvenNumber(int num){
    Console.WriteLine("輸入的是偶數:"+num);
}

分別將委派指向兩個方法

void Main(){
    int num = int.Parse(Console.ReadLine());
    PrintNumber print;
    if(num % 2 == 1){
        print = PrintOddNumber;
        //正規寫法是print = new PrintNumber(PrintOddNumber)
        //上面是語法糖(寫起來比較簡潔)
    }
    else
    { 
        print = PrintEvenNumber;
    }
    
    print(num);
}

https://ithelp.ithome.com.tw/upload/images/20200908/20109549FkAA7eLk9l.png

啊我就直接在if else裡面呼叫方法就好了啊
事實上委派更常出現在跨類別的函式傳遞
或者式Callback裡
讓我們繼續看下去

事件

麻煩請大家移駕到C# 事件(上) - 使用委派來實作事件去觀看
我覺得這篇文章對於事件的解釋非常容易了解

Action<>與Func<T,TRESULT>

Action<> 跟 Func<TResult> 最大的差異是

  • Action 所代表的是不傳回值的涵式(也就是void),<T1,T2> 代表傳入類型 若不傳入直接宣告Action即可
  • Func 則是會傳回值,我們會將回傳型態放在最後一項,<T1,T2,TResult> T1,T2代表傳入類型 TResult為回傳類型

若是不熟悉T請看上篇[Day7] 豪想要一種能接納所有人的型別 關於多載與泛型

它們一樣是委派

舉例來說
不帶參數的Action

using System;

namespace ConsoleApp1
{
    class Program
    {
        static Action Test;
        static void Main()
        {
            Test = Print;
            Test();
        }
        public static void Print()
        {
            Console.WriteLine("你好");
        }
    }
}
//你好

帶參數的Action<int,int>
這邊的<int,int> 對應到 Add跟Sub的傳入參數

using System;

namespace ConsoleApp1
{
    class Program
    {
        static Action<int,int> Calculate;
        static void Main()
        {
            Calculate = Add;
            Calculate(6, 5); //傳入型態為2個int 會執行 Add(5,6)
            //num1 + num2 = 11
            Calculate = Sub;
            Calculate(6, 5); //會執行 Sub(5,6)
            //num1 - num2 = 1
        }

        public static void Add(int num1, int num2)
        {
            Console.WriteLine($"num1 + num2 = {num1 + num2}");
        }
        public static void Sub(int num1, int num2)
        {
            Console.WriteLine($"num1 - num2 = {num1 - num2}");
        }
    }
}

不帶參數的Func<TResult>
這邊TResult對應回傳的string

using System;

namespace ConsoleApp1
{
    class Program
    {
        private static Func<string> func;
        static void Main()
        {
            func = GetHandSomeBoy;
            func();
        }

        public static string GetHandSomeBoy()
        {
            return "Me";
        }
    }
}
//Me

帶傳入參數Func<int,int,string>
回傳類型為string 傳入參數為int,int

using System;

namespace ConsoleApp1
{
    class Program
    {
        private static Func<int,int,string> Calculate;
        static void Main()
        {
            Calculate = Add;
            Console.WriteLine(Calculate(6, 5)); //傳入型態為2個int 會執行 Add(5,6)
            //num1 + num2 = 11
            Calculate = Sub;
            Console.WriteLine(Calculate(6, 5)); //會執行 Sub(5,6)
            //num1 - num2 = 1
        }

        public static string Add(int num1, int num2)
        {
            return $"num1 + num2 = {num1 + num2}";
        }
        public static string Sub(int num1, int num2)
        {
            return $"num1 - num2 = {num1 - num2}";
        }
    }
}

Action 跟 Fun 最高支援傳入16個參數的Method
Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult> 代理人

講這麼多 跨類別的傳遞呢(/‵Д′)/~ ╧╧)

這不就來了嗎
事實上在事件裡面的那篇文章就是跨類別的傳遞
我根據他的例子簡單改寫成Action 與 Func

有一個路人類別
他有一個方法負責接收消息
Passenger.cs

using System;

namespace ConsoleApp1
{
    public class Passenger
    {
        public void ReceiveNews(string news)
        {
            Console.WriteLine($"我收到一則新聞內容是:{news}");
        }
    }
}

還有我們的八卦版
負責放出新聞
Gossiping.cs

using System;

namespace ConsoleApp1
{
    public class Gossiping
    {
        public void Notify(Action<string> action, string news)
        {
            action(news);
        }
    }
}

在某個小島國裡有一個八卦中心跟一名路人
八卦中心透過路人接受訊息的能力傳了一則訊息給他
Taiwan.cs

namespace ConsoleApp1
{
    public class Taiwan
    {
        static void Main()
        {
            Gossiping gossiping = new Gossiping();
            Passenger passenger = new Passenger();

            gossiping.Notify(passenger.ReceiveNews, "開放美豬進口了");
        }
    }
}

https://ithelp.ithome.com.tw/upload/images/20200908/201095495EMzDtOj0o.png

透過接收者的方法不同能做不同的處理
我們再新增2個類別 一樣有他們自己的方法
FourPercent.cs

using System;

namespace ConsoleApp1
{
    public class FourPercent
    {
        public void Argue(string message)
        {
            Console.WriteLine($"政府要{message},政府有夠爛");
        }
    }
}

Green.cs

using System;

namespace ConsoleApp1
{
    public class Green
    {
        public void Support(string message)
        {
            Console.WriteLine($"Green:政府要{message},FourPercent好了拉,又死不了");
        }
    }
}

修改下Taiwan.cs 讓gossiping通知所有人

namespace ConsoleApp1
{
    public class Taiwan
    {
        static void Main()
        {
            Gossiping gossiping = new Gossiping();
            Passenger passenger = new Passenger();
            FourPercent fourPercent = new FourPercent();
            Green green = new Green();

            string news = "開放美豬進口了";
            gossiping.Notify(passenger.ReceiveNews, news);
            gossiping.Notify(fourPercent.Argue, news);
            gossiping.Notify(green.Support, news);
        }
    }
}

https://ithelp.ithome.com.tw/upload/images/20200908/20109549laik9qp5GG.png

我把本篇的範例放在( https://github.com/peace920902/ActionTutorial )


上一篇
[Day7] 豪想要一種能接納所有人的型別 關於多載與泛型
下一篇
[Day9] Lambda 表示式(匿名委派) 與 LINQ
系列文
C# 從入門到WebApi30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言