2016年7月13日 星期三

[轉貼] 茅塞頓開的一晚-Func 委派+匿名方法+lambda

出處:https://dotblogs.com.tw/lastsecret/2010/06/26/16201

茅塞頓開的一晚-Func 委派+匿名方法+lambda
今天聽了同事講解Func,聽完覺得太酷了。
不過在紀錄之前,要先講一下 委派跟匿名方法。

委派在我自己的理解上,就是拜託他去幫你執行某個方法
當然拜託也要拜託對人,你所拜託的委派,要能做到你所要的方法(也就是傳回的型別,跟傳入的參數要一致)
這個想法還滿直觀的,例如現實中,你必須拜託某個朋友幫你挑女友的生日禮物
你一定挑個要嘛交過很多女朋友的人,不然就是挑個女生,比較懂該送甚麼
總不可能挑個阿宅,只想的到送遊戲點卡,還是漫畫之類的吧。當然修電腦找這種人不錯(我就是這個好人..)。
所以在建立委派時,這委派必須符合的你方法。
image

class Program
{
    static void Main(string[] args)
    {
        //讀進要做的動作" + , - , * , / "
        string s = Console.ReadLine();

        //建立一個委派
        MyDelegate d;

        //依照傳入的動作,選擇要傳入委派的方法
        switch (s)
        {
            case "+":
                d = new MyDelegate(加法);
                break;
            case "-":
                d = new MyDelegate(減法);
                break;
            case "*":
                d = new MyDelegate(乘法);
                break;
            case "/":
                d = new MyDelegate(除法);
                break;
            default:
                d = new MyDelegate(加法);
                break;
        }

        //使用該委派
        int Answer = d(5, 2);

        Console.WriteLine(Answer);
        Console.ReadKey();
    }

 //用static是因為我懶的new出物件,但要視情況用
    public static int 加法(int x, int y)
    {
        return (x + y);
    }

    public static int 減法(int x, int y)
    {
        return (x - y);
    }

    public static int 乘法(int x, int y)
    {
        return (x * y);
    }

    public static int 除法(int x, int y)
    {
        if (y != 0)
            return (x / y);
        return 0;
    }

    public delegate int MyDelegate(int x, int y);

}
上面的因為都是基本觀念,所以就咻咻咻的講完了。
接著講匿名方法,匿名方法就是不需要為了可能只用一次的方法
而建立類別實體和該方法,直接經由delegate關鍵字將方法傳入即可。
跟javascript的function有點類似,有時只用一次的function 就直接 xxx.click( function(){…})使用
因此改寫上面的Code 12~29行那段

switch (s)
{
    case "+":
        d = delegate(int x, int y)
        {
            return x + y;
        };
        break;
    case "-":
        d = (int x, int y) => { return x - y; };
        break;
    case "*":
        d = (int x,int y) => x * y;
        break;
    case "/":
        d = (x, y) => y != 0 ? x / y : 0;
        break;
    default:
        d = new MyDelegate(加法);
        break;
}
加法是用delegate關鍵字,然後傳入參數並在中括號中 return結果
減法省略delegate關鍵字,改用Lambda運算式的寫法
乘法連中括號和return都省了,直接寫要傳回的結果
除法連傳入的型別都可省略,因Lambda會自動推斷正確的型別
到這邊,就講完了委派、匿名方法,還提到一點Lambda了。
Lambda很好用,不過看起來太精簡所以有點難懂,基本格式是
(input parameters)  =>  { expression }
左邊想成傳入方法的參數,用  " => " 運算子連接,右邊是 方法的內容。

接著講 Func 這個東西
依照昨天為了打個球跟我在外面流浪一整晚的同事(他不想曝光)的說法,
Func是微軟定義好的delegate
因此他跟delegate一樣
可以替以他為型別的變數指派一個方法
可以替以他為型別的變數指派一個方法
可以替以他為型別的變數指派一個方法
可以替以他為型別的變數指派一個方法
來看一個例子

public static int MyFunc(Func<int, int, int> fun, int x, int y)
{
    return fun(x, y);
}
上面這個方法有三個參數,分別是 Func<int,int,int> fun, int x ,int y
第一個參數就是func,記得上面repeat好幾次的那句話嗎?Func可以替以他為型別的變數指派一個方法
所以就把 fun 當成一個可以 丟入方法的變數 來看,
而fun這個方法呢,要傳入兩個int型別的參數,並且回傳int型別的回傳值
image
接著看完整的範例

class Program
    {
        static void Main(string[] args)
        {
            string s = Console.ReadLine();

            Func<int, int, int> f;

            //   依照輸入的運算符號選擇要存的方法
            //   記得!!Func<>是可以存方法的變數,
            //   所以+我存了一個加法的方法
            //   -是用delegate存匿名方法
            //   *、/ 是用lambda存方法
            switch (s)
            {
                case "+":
                    f = 加法;
                    break;
                case "-":
                    f = delegate(int x, int y) { return x - y; };
                    break;
                case "*":
                    f = (int x, int y) => x * y;
                    break;
                case "/":
                    f = (x, y) => y != 0 ? x / y : 0;
                    break;
                default:
                    f = 加法;
                    break;
            }
            //使用MyFunc方法
            int answer = MyFunc(f, 5, 2);
            
            
            //也可以這樣寫喔,傳入匿名的Func
            //int answer = MyFunc((x, y) => x + y, 5, 5);
            
            
            Console.WriteLine(answer);
            Console.ReadKey();
        }

        public static int MyFunc(Func<int, int, int> fun, int x, int y)
        {
            return fun(x, y);
        }

        public static int 加法(int x, int y)
        {
            return (x + y);
        }
    }
呼,講完了。
整個過程不太好懂,因為等於是方法中又有方法。
但懂了之後,覺得有很趣。

雖然現在經驗不足,還不知道要用在哪裡比較適當,但學起來再說。