iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 17
1

委派

「委派型別」代表對方法的參考,其中含有特定參數清單與傳回型別。
委派讓您可將方法視為實體,而實體能指派給變數或當作參數來傳遞。
委派就類似其他程式設計語言中的函式指標,但與函式指標的不同之處是,委派是物件導向且為型別安全。

簡單來說 Delegate (委派)是將 function 當成參數傳遞的型別。

委派的方式有三種具名函式匿名函式Lambda運算式

  • 具名函式: 將已宣告的方法(nameMethod)指給委派

  • 匿名函式: delegate (arguments) { statements }

    • delegate: 匿名函式的的保留字

    • arguments: 傳入參數的宣告,可以多個參數(以,隔開)

    • statements: 此函式執行的程式碼片段

  • Lambda: arguments => expression | block

    • arguments: 傳入參數的宣告,可以多個參數(以,隔開)。

      • 只有一個參數時可以不用括號,多個參數時都要加上括號

      • 可以不用明確指定型別

      • 可以明確指定型別

      • 明確指定型別時一定要加上括號

      • 沒有傳入參數時用空括號 () 表示

    • expression: 運算式,不括大括號 {} ,只能單行程式碼,代表回傳值

    • block: { statements }: 程式碼區塊,statement為此函式執行的程式碼片段

具名函式

委派需要事先宣告此委派類型的傳入參數與傳回值,至少 0 個參數,至多 32 個參數,可以無傳回值,也可以指定傳回值類型。

注意 參數與傳回型別需一致

static void Main (string[] args) {
  MyDelegate d;
  d = new MyDelegate (add);
  var v = d (1, 2); // 1 + 2 = 3
  Console.WriteLine (v);
  d = new MyDelegate (subtract);
  v = d (2, 1); // 2 - 1 = 1
  Console.WriteLine (v);
}
public delegate int MyDelegate (int x, int y);
public static int add (int a, int b) {
  return a + b;
}
public static int subtract (int a, int b) {
  return a - b;
}

匿名函式

使用匿名方法可以減少在具現化委派中撰寫程式碼的額外負荷,因為不必另外建立一個方法。

static void Main (string[] args) {
  MyDelegate d;
  d = delegate (int x, int y) {
    return x + y;
  };
  var v = d (1, 2); // 1 + 2 = 3
  Console.WriteLine (v);
  d = delegate (int x, int y) {
    return x - y;
  };
  v = d (2, 1); // 2 - 1 = 1
  Console.WriteLine (v);
}
public delegate int MyDelegate (int x, int y);

Lambda

以 lambda 運算式取代匿名方法,成為撰寫程式碼內嵌的慣用方式。

static void Main (string[] args) {
  MyDelegate d;
  d = (int x, int y) => { return x + y; };
  var v = d (1, 2); // 1 + 2 = 3
  Console.WriteLine (v);
  d = (x, y) => x - y;
  v = d (2, 1); // 2 - 1 = 1
  Console.WriteLine (v);
}
public delegate int MyDelegate (int x, int y);

可以看到在減法方法中,lambda 甚至可以省去傳入參數的型別與 return 關鍵字。

委派的聲明

除了 Delegate 也可以使用 Action, Func, Predicate 這些類型來進行委派,不用事先宣告委派的類型。

Action

Action是無傳回值的泛型委派。

Action至少0個參數,至多16個參數。

static void Main (string[] args) {
  Action<string> action = action1;
  action ("123"); // 123
  action = action2;
  action ("Mio"); // Hi Mio
}
public static void action1 (string a) {
  Console.WriteLine (a);
}
public static void action2 (string a) {
  Console.WriteLine ("Hi " + a);
}

Func

Func 是有傳回值的泛型委派。

Func 至少0個參數,至多16個參數,以及一個傳回值的型別參數。方法必須有傳回值,不可為 void。

Func<int> 無輸入的參數,且傳回值為 int 型別的委派。

Func<object,string,int> 表示傳入參數為 object , string 傳回值為 int 型別的委派。

static void Main (string[] args) {
  Func<int> func = func1;
  Console.WriteLine (func ()); // 0
}
public static int func1 () {
  return 0;
}

使用 Func 委派時,也能用 Lambda 進行。

static void Main (string[] args) {
  Func<int> func = () => 0;
  Console.WriteLine (func ()); // 0
  Func<int, int, string> func2 = (a, b) => (a + b).ToString ();
  Console.WriteLine (func2 (1, 2)); // 1 + 2 = 3
}

Predicate

Predicate 是返回 bool 型的泛型委派。

Predicate<int> 表示傳入參數為 int 返回 bool 的委派。

Predicate 有且只有一個參數,傳回值固定為 bool。

static void Main (string[] args) {
  Predicate<int> predicate = predicate1;
  Console.WriteLine (predicate (0)); // True
}
public static bool predicate1 (int a) {
  return a == 0;
}

Lambda

這邊在多補充點有關 Lambda 的觀念,因為 Lambda 在目前的程式碼撰寫很容易會使用到。

Lambda 運算式是匿名函式,可用來建立委派運算式樹狀架構類型。
使用 Lambda 運算式可以撰寫區域函式,這些函式可以當做引數傳遞,或是當做函式呼叫的值傳回。
Lambda 運算式對於撰寫 LINQ 查詢運算式而言特別有用。

題外話:LINQ 會在之後做介紹。

建立 Lambda 運算式,需在 Lambda 運算子(=>) 的左邊指定輸入參數 (如果有的話),並將運算式或陳述式區塊放在另一邊。

結構如:arguments => expression | block

(=>) 運算子與指派 (=) 具有相同的優先順序。

arguments

  • 只有一個傳入參數時可以省略小括弧 ()
//Only one input parameter
x => x * x  //legal
(x) => x * x    //legal

//Two or more input parameters
x, y => x * y   //illegal
(x, y) => x * y //legal
  • 可以明確指定型別,明確指定型別時一定要加上括號 () ,不明確指定型別也可以
string x => x * x   //illegal
(string x) => x * x //legal
x => x * x
  • 沒有傳入參數時以空括號 () 表示
() => Console.WriteLine("Hello Lambda")

expression

  • 只有單行程式碼時為 expression ,可以不用 {} 包住程式,程式行的最後也不用加 ; ,其程式碼所代表的是回傳值
x => x * x  //delegate (int x) { return x * x; }
  • 使用 expression 格式的Lambda稱為Lambda運算式(Lambda Expressions)

block

  • 一般的程式碼區塊,可以多行程式碼,每行最後要加 ;
x => { return x * x; }
  • 使用 block 格式的Lambda稱為Lambda陳述式(Lambda Statements)

一些小結論

今天介紹了委派,其實有點算是講古吧,但如果沒有基本觀念的話,直接使用也容易搞不懂在用什麼。上面也講過了,現在主流都是用 Lambda 了,尤其是操作 LINQ 時,幾乎每一句都是,所以 Lamda 是什麼,這邊也先告訴你了。

Lambda 也可以在宣告方法時使用如

static int add2num (int a, int b) => a + b;

所以與其說是教委派,更多的是 Lambda 的使用,但如果沒有委派的觀念,使用起 Lambda 時總會卡手卡腳的。

本來今天是想介紹泛型的,打到一半覺得還是先講委派好了,泛型後面接集合比較順,也可以不用再看一次類別。

感謝閱讀。

參考連結
C# 委派 - C# 語言教學課程 | Microsoft Docs
匿名方法 (C# 程式設計手冊) | Microsoft Docs
Lambda 運算式 (C# 程式設計手冊) | Microsoft Docs
C#委託的介紹(delegate、Action、Func、predicate) @ 資訊園 :: 痞客邦 ::


上一篇
DAY 16 類別(五)
下一篇
DAY 18 泛型
系列文
.net 後端起手式 30天認識 C# 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言