委派 Delegate
講的簡單一點 他就像一個轉接頭
你充 iphone 需要轉接頭 但轉接頭的兩端能插什麼線是固定的
你買了 Type-C 的線 就不可能插進 Micro USB 的孔
但也就是說 只要是插的進去的線 就都可以讓你充電
今天你有錢可以用原廠的線 明天比較窮就用夜市牌的
反正只要插的進轉接器 就是可以用的線
接下來我們把上面這個例子轉成程式碼
我用紅色框起來的兩個框框 指的就是轉接頭的兩端
只要兩端一模一樣 就表示符合這個轉接頭的規定 表示這條線可以用
也就是說 今天我們轉接頭 = 委派 = Delegate 的規定是 => 輸入string 輸出 int
你可以寫 100 個符合這樣規定的函式 隨你今天心情決定你轉接頭另一邊要放哪一個
有錢時就用原廠線 沒錢時就用夜市牌
反正只要是輸入string 輸出 int 的 都可以被這個 轉接頭 = 委派 = Delegate 使用
有了這個概念 接下來我們就可以看程式碼了
namespace ConsoleApplication9
{
/// <summary>
/// //重點 => BindFunc, GetHeight, GetWeight 三者簽章需一致 => 都是輸入 string 傳回 int
/// </summary>
class Program
{
//我做了一個轉接頭 規定只能插入輸入 string 輸出 int 的線
delegate int InStrOutInt(string name);
static void Main(string[] args)
{
InStrOutInt a = new InStrOutInt(GetHeight); //傳統寫法
InStrOutInt b = GetWeight; //語法糖
//也可以用lambda來做匿名委派 => 就是明明是一個函式卻沒有取名的意思 => 具名函式就像下面的 GetHeight, GetWeight
InStrOutInt c = name =>
{
return -1;
};
//呼叫
var resultA = a("Tom"); //其實是呼叫 GetHeight 但我透過轉接頭 a 來間接呼叫他
var resultB = b("Tom"); //其實是呼叫 GetWeight 但我透過轉接頭 b 來間接呼叫他
var resultC = c("Tom"); //其實是呼叫 一個匿名函式 但我透過轉接頭 c 來間接呼叫他
}
static int GetHeight(string name)
{
return 180;
}
static int GetWeight(string name)
{
return 70;
}
}
}
而 Lambda 到底是什麼... 大家可以在網路上找到很多文章仔細講解
或是你直接把他當作讓委派的程式碼變的更精簡的東西即可 如同大家使用 LINQ 一樣
但聰明如你馬上就會發現
如上面例子 那我就直接寫死 一個 call GetHeight()、另一個 call GetWeight() 就好
何必自找麻煩 又做了一個轉接器 ( 委派 ) 多繞一圈 讓程式碼多出這麼多行?
所以請考慮下面這個例子
namespace ConsoleApplication9
{
class Program
{
//我做了一個轉接頭 規定只能插入輸入 string 輸出 int 的線
delegate int InStrOutInt(string name);
static void Main(string[] args)
{
InStrOutInt func = name =>
{
return -1;
};
var text = "體重"; //user輸入身高=>得180, 輸入體重=>得70, 其他=>得-1
switch (text)
{
case "身高":
func = new InStrOutInt(GetHeight);
break;
case "體重":
func = GetWeight;
break;
}
//會變的只有 user 輸入的值 = 他要查什麼 但取得輸出結果的程式碼永遠一樣 就是 func("Tom")
var result = func("Tom");
}
static int GetHeight(string name)
{
return 180;
}
static int GetWeight(string name)
{
return 70;
}
}
}
那我們再來看一個簡單的例子 想必大家都寫過 Webform 程式吧
假設我只做了一個按鈕 一按就會輸出 '你好'
想必大家都知道程式碼長成怎樣
using System;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
Response.Write("你好");
}
}
但大家有沒有想過 為什麼他會知道我按下那個按鈕 他要來幫我 call 這個 Button1_Click 的函式?
要解答這個問題很簡單 我們直接把這個函式從 .cs 檔中刪掉 故意讓他壞掉
就會看到大家很熟悉的畫面
如果我們點開看完整內容的話會發現
+= 這個詞 也是一種掛載委派的方式 是他幫我們做了按下按鈕要去呼叫哪個事件的這件事
而想想這帶給我們什麼好處?
.cs檔中看起來更精簡了 我只要專注於開發按下按鈕時要發生的 '那件事' => 商業邏輯
但按哪個按鈕觸發事件的關係 我可能不用知道 別人會幫我負責 或是雖然我知道 但程式碼不用寫在這邊
這也就是寫死 與不寫死之間的差異 = 委派之所以有彈性的地方
接下來再稍微講解一下 Lambda 的寫法
namespace ConsoleApplication9
{
class Program
{
delegate int In2StrOutInt(string name, string id);
static void Main(string[] args)
{
//兩個參數輸入時 lambda 匿名委派這樣寫
In2StrOutInt func2 = (name, id) =>
{
return -2;
};
}
}
}
using System;
namespace ConsoleApplication9
{
class Program
{
static void Main(string[] args)
{
//用 lambda 寫了一個沒有名子的函式 = 匿名函式
//輸入 string ( name ),輸出 int,
//並且把這個沒有名子的函式委派給 tmpFunc (泛型委派)
Func<string, int> tmpFunc = name =>
{
return -3;
};
var result = tmpFunc("Tom"); //得-3
}
}
}
using System;
namespace ConsoleApplication9
{
class Program
{
static void Main(string[] args)
{
//沒有輸入參數時這樣寫 => 只寫()
Func<int> tmpFunc = () => -4; //當函式主體只有一行時 連{} 還有return 都可以省略
var result = tmpFunc(); //得-4
}
}
}
若你想知道背後的原理 請看