iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 18
1

泛型

泛型將型別參數的概念引進 .NET Framework 中,使得類別和方法在設計時,可以先行擱置一或多個類型的規格,直到用戶端程式碼對類別或方法進行宣告或具現化時再行處理。

有些時候,你可能覺得這個類別除了 int 能用,其他型別應該也能用才對,但如果要換成其他型別時,要把相同的東西複製貼上,有點蠢之外,改寫方法時還要兩邊一起改,超級麻煩,如果你有這種想法時,就可以來了解什麼是泛型。

以下概觀皆引用至官方

  1. 使用泛型型別以最佳化程式碼重複使用、型別安全和效能。

  2. 泛型的最常見用法是建立集合類別。

  3. .NET Framework 類別庫包含 System.Collections.Generic 命名空間中的數種新泛型集合類別。 您應該盡可能使用這些類別,而不是使用類似在 System.Collections 命名空間中的 ArrayList 類別。

  4. 您可以建立自己的泛型介面、類別、方法、事件和委派。

  5. 泛型類別可限制為允許存取特定資料類型上的方法。

  6. 泛型資料類型中所使用的類型相關資訊,可在執行階段透過反映取得。

我們先來建立一個泛型類別,然後使用這個泛型類別。

先從簡單的開始,我們想要一個可以存放任意單一型別並能紀錄上一個一樣類別的類別,可以想像成是一台裝載一模一樣貨物的火車

class Program {
  static void Main (string[] args) {
    Node<int> head = new Node<int> (0, null);
    Node<int> node = new Node<int> (1, head);
    Node<string> head2 = new Node<string> ("Mio", null);
    Node<string> node2 = new Node<string> ("Miffy", head2);
  }
}
class Node<T> {
  public Node (T value, Node<T> next) {
    Value = value;
    Next = next;
  }
  T Value;
  Node<T> Next;
}

這樣我們就簡單完成一個泛型類別了,Value 裝載我們想要存放的型別,Next 則是記錄它所連接的車廂。

而由於車頭不會有上一節車廂,所以給它一個初始值,但每個車頭都要特地給予初始值,此時你可能想那就再建立一個建構式,如果是車頭就使用那個建構式,Next 就自動指向類別初始值也就是 null。

但如果想要讓車頭的內容也是初始值呢?可是我們不曉得型別是什麼啊,如果不小心給 int 一個 null 值程式便會掛掉,這時我們就可以來使用 default 的另一種用法了。

題外話:使用泛型時,是有官方的型別參數命名方針,這邊就不多贅述了,文末。

class Program {
  static void Main (string[] args) {
    Node<int> head = new Node<int> ();
    Node<int> node = new Node<int> (1, head);
    Node<string> head2 = new Node<string> ();
    Node<string> node2 = new Node<string> ("Mio", head2);
  }
}
class Node<T> {
  public Node () {
    Value = default (T);
    Next = null;
  }
  public Node (T value, Node<T> next) {
    Value = value;
    Next = next;
  }
  T Value;
  Node<T> Next;
}

使用 default 就可以指定任何型別得初始值了。

條件約束

條件約束會通知編譯器有關型別引數必須要有的功能。
如果沒有任何條件約束,則型別引數可以是任何型別。

但因為舉例的例子真的很簡單,所以用起來有點奇怪就以最簡單的條件約束來說,struct 型別引數必須是實值型別、class 型別引數必須是參考型別。

以 struct 來把上面的程式修改一下

class Node<T> where T : struct {
  public Node () {
    Value = default (T);
    Next = null;
  }
  public Node (T value, Node<T> next) {
    Value = value;
    Next = next;
  }
  T Value;
  Node<T> Next;
}

可以看到參考型別的 head2 便會報錯,因為 string 並不是實質型別。

更多的條件約束請自行到文末觀看。

泛型在使用時,並不是只能有一個參數,也可以使用多重參數

class Program {
  static void Main (string[] args) {
    Node<int, int> head = new Node<int, int> ();
    Node<int, int> node = new Node<int, int> (1, 6, head);
    Node<string, int> head2 = new Node<string, int> ();
    Node<string, int> node2 = new Node<string, int> ("Mio", 2, head2);
  }
}
class Node<T, U> {
  public Node () {
    Value = default (T);
    Value2 = default (U);
    Next = null;
  }
  public Node (T value, U value2, Node<T, U> next) {
    Value = value;
    Value2 = value2;
    Next = next;
  }
  T Value;
  U Value2;
  Node<T, U> Next;
}

這樣就是可以裝載兩種型別的火車了。

一些小結論

本篇文章其實並不是教導你如何建立泛型,建立一個好的泛型我也不太會,所以重點是放在泛型的基本知識,明天介紹集合類別時才不容易不清楚,但應該也不用怕不懂,看過滿多人不懂但還是用得好好的,所以就當作看安心的吧。

感謝閱讀。

參考連結
泛型 (C# 程式設計手冊) | Microsoft Docs
泛型型別參數 (C# 程式設計手冊) | Microsoft Docs
型別參數的條件約束 (C# 程式設計手冊) | Microsoft Docs


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

尚未有邦友留言

立即登入留言