開發人員一直使用 .NET 中的泛型,無論是隱含還是明確。 當您在 .NET 中使用 LINQ 時,您是否注意到您正在使用 IEnumerable<T>? 或者,如果您曾經看到使用 Entity Framework 和資料庫互動的「通用儲存庫」線上範例,您有沒有注意到大部分的方法都會傳回 IQueryable<T>
呢? 您可能想知道這些範例中的 T 是什麼,以及它在那裡的原因。
首先在 .NET Framework 2.0 中引進,泛型基本上是「程式代碼範本」,可讓開發人員定義 型別安全 的數據結構,而不需要認可實際數據類型。 例如,是一個List<T>,可以宣告及與任何類型搭配使用,例如List<int>
、 List<string>
或 List<Person>
。
若要了解為什麼泛型很有用,讓我們來看看新增泛型之前和之後的特定類別: ArrayList。 在 .NET Framework 1.0 中,元素 ArrayList
的類型為 Object。 加入至集合的任何項目會以無訊息方式轉換成 Object
。 從清單中讀取元素時,也會有相同的情況發生。 此過程稱為裝箱和拆箱,且會影響效能。 不過,除了效能之外,在編譯時期無法判斷清單中的數據類型,這會使程式碼變得很脆弱。 泛型可藉由定義每個清單實例將包含的數據類型來解決此問題。 例如,您只能將整數新增至 List<int>
,而且只能將 Person 新增至 List<Person>
。
泛型也可以在執行時使用。 運行時間知道您所使用的數據結構類型,而且可以更有效率地將它儲存在記憶體中。
下列範例是一個小程式,說明在運行時間了解數據結構類型的效率:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace GenericsExample {
class Program {
static void Main(string[] args) {
//generic list
List<int> ListGeneric = new List<int> { 5, 9, 1, 4 };
//non-generic list
ArrayList ListNonGeneric = new ArrayList { 5, 9, 1, 4 };
// timer for generic list sort
Stopwatch s = Stopwatch.StartNew();
ListGeneric.Sort();
s.Stop();
Console.WriteLine($"Generic Sort: {ListGeneric} \n Time taken: {s.Elapsed.TotalMilliseconds}ms");
//timer for non-generic list sort
Stopwatch s2 = Stopwatch.StartNew();
ListNonGeneric.Sort();
s2.Stop();
Console.WriteLine($"Non-Generic Sort: {ListNonGeneric} \n Time taken: {s2.Elapsed.TotalMilliseconds}ms");
Console.ReadLine();
}
}
}
此程式會產生類似下列的輸出:
Generic Sort: System.Collections.Generic.List`1[System.Int32]
Time taken: 0.0034ms
Non-Generic Sort: System.Collections.ArrayList
Time taken: 0.2592ms
您可以在這裡注意到的第一件事是,排序泛型清單比排序非泛型清單要快得多。 您可能也會注意到泛型清單的類型是相異的 ([System.Int32]),而非泛型清單的類型則會一般化。 因為執行階段知道泛型 List<int>
的類型 Int32,所以它可以將清單項目儲存在記憶體中的基礎整數陣列中,而非泛型 ArrayList
則必須將每個清單項目轉換成物件。 如此範例所示,多餘的類型轉換會佔用時間並使清單排序速度變慢。
執行階段知道您的泛型類型的另一個優勢是更好的除錯效果。 當您在 C# 中偵錯泛型時,您知道每個元素在數據結構中的類型。 如果沒有泛型,您就不知道每個元素的類型為何。