前言

以前學 Generic,我只記住一句話:

Generic 可以重複利用程式碼。

這句話沒有錯,但一直讓我無法真正理解 Generic 的設計目的。

直到我開始站在 Framework 設計者 的角度思考,我才發現:

Generic 真正想解決的是「保留真正的型別」。


如果沒有 Generic,我會怎麼設計?

假設今天要交換兩個變數。

第一個想到的方法就是每個型別都寫一份。

void Swap(int a, int b)
{
}

void Swap(string a, string b)
{
}

void Swap(Student a, Student b)
{
}

問題就在,如果有 100 種型別,就要寫 100 個版本。

因此想要有個方法能夠接受任意型別的參數。


那全部改成 object 不就好了?

第二個想到的方法就是:

void Swap(object a, object b)
{
}

這方法能夠接受所有型態,但是會有問題。

Value Type 需要 Boxing

int x = 10;

object obj = x;

CLR 必須建立一個新的 Heap Object,因此會產生 Boxing。


取出時需要 Unboxing

int x = (int)obj;

而且還要自己強制轉型,如果轉錯型別,就會在 Runtime 發生例外。


沒有 Type Safety

例如:

Swap(student, 10);

編譯器不知道這是不是合法,很多錯誤只能等到執行時才發現。


Generic 到底解決了什麼?

這時我才理解 Generic 的真正目的。

它不只讓程式碼比較少。

而且還有以下特點:

  • 保留真正的型別
  • Compile Time 就能做型別檢查
  • 避免 Boxing / Unboxing
  • 一份程式碼可以支援多種型別

T 到底是什麼?

以前一直把 T 看成一種特殊語法。

現在我的理解變成:

T 就是一個目前還不知道的型別。

例如:

void Swap<T>(ref T a, ref T b)
{
}

當呼叫:

Swap<int>(ref x, ref y);

編譯器就知道:

void Swap(ref int a, ref int b)
{
}

如果呼叫:

Swap<Student>(ref student1, ref student2);

就等於:

void Swap(ref Student student1, ref Student student2)
{
}

因此:Generic 並不是 object。

它只是把真正的型別延後到使用時才決定。


我最大的誤解

以前我一直以為:

Generic = 重複利用程式碼。

現在我的理解變成:

Generic = 不知道型別,但仍然保留真正的型別。

這兩句話看起來很像。

但後者才是真正的設計理念。


我的學習心法

我現在遇到 Generic,都只會問自己一個問題:

如果不用 Generic,我會怎麼做?

答案通常只有兩種。

方法一

每個型別都寫一次。

缺點:

  • 程式碼大量重複

方法二

全部改成 object。

缺點:

  • Boxing
  • Unboxing
  • Runtime Cast
  • 沒有 Type Safety

Generic

保留真正型別,同時避免以上所有問題。

一句話記住 Generic

不知道型別,但保留真正的型別。


快速複習

沒有 Generic,我會怎麼做?

  • 每個型別寫一份
  • 全部改成 object

object 有什麼問題?

  • Boxing
  • Unboxing
  • Runtime Cast
  • 沒有 Type Safety

Generic 解決了什麼?

  • 保留真正型別
  • Compile Time 型別檢查
  • 避免 Boxing / Unboxing
  • 一份程式支援所有型別