泛型 (Generics)

設計類別或是方法的時候,泛型技術允許程式設計人員暫時忽略型別規格定義,直到用戶端程式碼宣告時再設定相關型別,避免因為不相容的轉型導致可能的錯誤。

泛型被廣泛的運用在集合類別的設計,以提供不定型別的集合設計支援。

.NET傳統的非泛型集合類別,僅針對 object 型態的物件進行操作,所有加入集合的物件元素均直接參照 object 型別,因此一旦資料加入集合會被當作 object 型別物件作處理,所有物件資訊(例如資料型態)全部都會消失。

由於 object 是所有類別的基礎型別,因此支援任意型別,如此一來,當物件從集合中取出時,必須精確的鑄型為物件的真實型別,此時就可能發生轉型錯誤。
// UCastError
class Program
{
    static void Main(string[] args)
    {
        Queue queue = new Queue();
        queue.Enqueue("ABCDE");
        queue.Enqueue(10);
        object firstObject = queue.Dequeue();
        object secondObjct = queue.Dequeue();
        string strValue = firstObject.ToString();
        int intValue = int.Parse(secondObjct.ToString());
        Console.WriteLine("第一個集合物件:" + strValue);
        Console.WriteLine("第二個集合物件:" + intValue);
        Console.Read();
    }
}
於 Queue 集合 ueue 分別加入兩個不同型別的資料,字串「ABCDE」以及整數 10 ,接下來取出時分別轉型為原來的型別 – string 以及 int,執行程式可以得到以下的輸出結果:

於 Queue 集合 ueue 分別加入兩個不同型別的資料,字串「ABCDE」以及整數 10 ,接下來取出時分別轉型為原來的型別 – string 以及 int,執行程式可以得到以下的輸出結果:
第一個集合物件:ABCDE
第二個集合物件:10
重新調整其中讀取元素的過程,直接將取出的物件儲存至指定的型別變數:
string firstObject = theQueue.Dequeue();
int secondObjct = theQueue.Dequeue();
編譯程式將得到以下的錯誤訊息:
錯誤 CS0266 無法將類型 'object' 隱含轉換成 'string'。已存在明確轉換 (是否漏了轉型?)
錯誤 CS0266 無法將類型 'object' 隱含轉換成 'int'。已存在明確轉換 (是否漏了轉型?)

由於類別本身僅接受 object 型態物件,必須透過轉型才能以正確的型態進行運算,在這種情形下,很有可能因為不當的轉型而導致程式錯誤。

.NET Framework 類別庫裏,命名空間 System.Collections.Generic 支援泛型版本的基礎集合類別,透過型別限定避免可能發生的轉換錯誤。
// GenericDemo
using System;
using System.Collections.Generic;
class Program
{
    static void Main(string[] args)
    {
        Queue queue = new Queue();
        queue.Enqueue("ABCDE");
        queue.Enqueue("10");
        string firstObject = queue.Dequeue();
        string secondObjct = queue.Dequeue();
        Console.WriteLine("第一個集合物件:" + firstObject);
        Console.WriteLine("第二個集合物件:" + secondObjct);
        Console.Read();

    }
}
首先引用泛型命名空間 System.Collections.Generic ,宣告 string 型別的泛型版本 Queue 變數。

由於指定了 string 型別,因此 Enqueue 只能接受 string 參數,引用 Dequeue 取出的內容則是明確的 string 型別元素,指定給 string 變數即可。

泛型集合宣告必須在類別名稱之後,於角型符號中設定要指定的型別,語法如下:
GClass generic = new  GClass() ; 
其中的 T表示要指定的型別名稱,此範例的泛型集合 queue 設定為 string ,只允許存取 string 型別物件,因此也不會有轉型的需要,這是泛型與傳統集合最主要的差異。
現在作個實驗,考慮以下這一行程式碼:
queue.Enqueue(10);
其中的參數 10 是一個 int 數值,此行程式碼將導致以下的錯誤:
錯誤 2 引數 1: 無法從 'int' 轉換為 'string' …
如你所見,儲存至泛型集合中的物件,必須與宣告的型別相同,否則無法通過編譯。
應用程式的開發過程中,建議直接使用泛型版本的集合,這也是編輯器的預設行為,當你建立一個專案時,會發現預設載入的命名空間如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
其中第 2 行引用的是 System.Collections.Generic 而非 System.Collections。

除了命名空間要注意之外,同時透過 <> 指定要操作的泛型版本,除此之外,泛型集合與一般集合的使用無異,請自行嘗試。




沒有留言: