泛型是 C# 2.0 引入的核心特性,它允許在定義類、接口、方法、委托等時使用未指定的類型參數,在使用時再指定具體類型。這種機制可以顯著提高代碼的復用性、類型安全性和性能。
一、泛型的核心概念
類型參數化
泛型允許將類型作為 "參數" 傳遞給類、方法等,就像方法可以接受數值參數一樣。例如,List<T>
?中的?T
?就是類型參數,使用時可以指定為?List<int>
、List<string>
?等。編譯時類型檢查
泛型在編譯階段就會進行類型驗證,避免了運行時的類型轉換錯誤。例如,List<int>
?只能存儲?int
?類型,編譯器會阻止添加字符串等其他類型。消除裝箱 / 拆箱操作
對于值類型(如?int
、double
),非泛型集合(如?ArrayList
)會將值類型裝箱為?object
,取出時再拆箱,造成性能損耗。泛型集合(如?List<int>
)直接存儲值類型,避免了這一過程。代碼復用
一套泛型代碼可以適配多種數據類型,無需為每種類型重復編寫邏輯(如排序、查找等)。
二、泛型的基本使用
1. 泛型類(Generic Classes)
泛型類是最常用的泛型形式,定義時在類名后添加?<類型參數>
,使用時指定具體類型。
// 定義泛型類
public class MyGenericClass<T>
{private T _value;public MyGenericClass(T value){_value = value;}public T GetValue(){return _value;}public void SetValue(T value){_value = value;}
}// 使用泛型類
var intContainer = new MyGenericClass<int>(10);
int intValue = intContainer.GetValue(); // 10var stringContainer = new MyGenericClass<string>("Hello");
string stringValue = stringContainer.GetValue(); // "Hello"
2. 泛型方法(Generic Methods)
泛型方法可以在普通類或泛型類中定義,方法名后添加?<類型參數>
,調用時可顯式或隱式指定類型。
public class GenericMethodExample
{// 定義泛型方法public T Max<T>(T a, T b) where T : IComparable<T>{return a.CompareTo(b) > 0 ? a : b;}
}// 使用泛型方法
var example = new GenericMethodExample();
int maxInt = example.Max(5, 10); // 10(隱式推斷類型為int)
string maxStr = example.Max<string>("apple", "banana"); // "banana"(顯式指定類型)
3. 泛型接口(Generic Interfaces)
泛型接口與泛型類類似,常用于定義集合、比較器等具有通用性的契約。?
// 定義泛型接口
public interface IRepository<T>
{T GetById(int id);void Add(T item);void Update(T item);void Delete(int id);
}// 實現泛型接口(以用戶倉儲為例)
public class UserRepository : IRepository<User>
{public User GetById(int id) { /* 實現 */ }public void Add(User item) { /* 實現 */ }public void Update(User item) { /* 實現 */ }public void Delete(int id) { /* 實現 */ }
}
4. 泛型委托(Generic Delegates)
泛型委托允許定義可接受不同類型參數的委托,C# 內置的?Func<T>
、Action<T>
?就是典型例子。
// 定義泛型委托
public delegate T Transformer<T>(T input);// 使用泛型委托
public class DelegateExample
{public static int Square(int x) => x * x;public static string ToUpper(string s) => s.ToUpper();
}// 調用
Transformer<int> intTransformer = DelegateExample.Square;
int result = intTransformer(5); // 25Transformer<string> stringTransformer = DelegateExample.ToUpper;
string upperStr = stringTransformer("hello"); // "HELLO"
5. 泛型約束(Constraints)
泛型約束用于限制類型參數的范圍,確保類型參數滿足特定條件(如必須實現某接口、必須是引用類型等)。常用約束如下:
約束語法 | 說明 | |
where T : struct | T ?必須是值類型(非?Nullable<T> ) | |
| T ?必須是引用類型 | |
where T : new() | T ?必須有公共無參構造函數 | |
where T : 基類名 | T ?必須是指定基類或其派生類 | |
| T ?必須實現指定接口 | |
where T : U | T ?必須是?U ?或其派生類(用于多參數) |
示例:
// 約束T必須實現IComparable<T>接口
public class GenericWithConstraint<T> where T : IComparable<T>
{public T FindMax(T[] items){if (items == null || items.Length == 0)throw new ArgumentException("數組不能為空");T max = items[0];foreach (var item in items){if (item.CompareTo(max) > 0)max = item;}return max;}
}
6. 泛型集合(Generic Collections)
.NET Framework 提供了豐富的泛型集合類(位于?System.Collections.Generic
?命名空間),替代了非泛型集合(如?ArrayList
、Hashtable
):
List<T>
:動態數組,替代?ArrayList
Dictionary<TKey, TValue>
:鍵值對集合,替代?Hashtable
HashSet<T>
:無序唯一元素集合Queue<T>
:先進先出(FIFO)隊列Stack<T>
:后進先出(LIFO)棧
示例:
using System.Collections.Generic;// 使用List<T>
var numbers = new List<int> { 1, 2, 3 };
numbers.Add(4);
int first = numbers[0];// 使用Dictionary<TKey, TValue>
var personAges = new Dictionary<string, int>
{{ "Alice", 30 },{ "Bob", 25 }
};
int aliceAge = personAges["Alice"];
三、泛型的高級特性
1. 泛型類型參數的協變與逆變
- 協變(Covariance):允許將泛型類型參數從派生類隱式轉換為基類,使用?
out
?關鍵字標記(僅適用于接口和委托)。 - 逆變(Contravariance):允許將泛型類型參數從基類隱式轉換為派生類,使用?
in
?關鍵字標記(僅適用于接口和委托)。
示例:
// 協變接口(out關鍵字)
public interface IEnumerable<out T> { ... }// 逆變接口(in關鍵字)
public interface IComparer<in T> { ... }// 用法
IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings; // 協變:string → objectIComparer<object> objectComparer = new ObjectComparer();
IComparer<string> stringComparer = objectComparer; // 逆變:object → string
2. 靜態字段與泛型
泛型類的靜態字段在不同封閉類型(如?MyClass<int>
?和?MyClass<string>
)中是獨立的,不會共享:
public class StaticGeneric<T>
{public static int Count { get; set; } = 0;
}// 不同類型的靜態字段獨立
StaticGeneric<int>.Count = 1;
StaticGeneric<string>.Count = 2;
Console.WriteLine(StaticGeneric<int>.Count); // 1(與string的Count無關)
List<int>
?本質上是泛型類?List<T>
?的一個實例化版本,它依賴于泛型機制才能存在。如果沒有?List<T>
?這個泛型定義,就無法通過傳入?int
?得到?List<int>
。
T
?是泛型的 "模板參數",而?int
?是填充這個模板的 "實際參數"。List<int>
?是泛型機制的產物,因此它是泛型集合的典型應用。
3. 泛型類型的反射
可以通過反射獲取泛型類型的信息,如類型參數、約束等:
Type listType = typeof(List<int>);
if (listType.IsGenericType)
{Type genericTypeDefinition = listType.GetGenericTypeDefinition(); // 得到List<T>Type[] typeArguments = listType.GetGenericArguments(); // 得到[int]
}
四、泛型的優勢總結
- 類型安全:編譯時檢查類型,避免運行時類型轉換錯誤。
- 性能優化:減少裝箱 / 拆箱操作,尤其對值類型更高效。
- 代碼復用:一套邏輯適配多種類型,減少重復代碼。
- 靈活性:結合約束、協變、逆變等特性,可適應復雜場景。
五、泛型的適用場景
- 集合類(如自定義列表、字典)
- 工具類(如轉換器、比較器)
- 數據訪問層(如通用倉儲模式)
- 委托和事件(如通用回調函數)
- 算法實現(如排序、搜索,適用于多種數據類型)
通過泛型,C# 代碼可以在保持類型安全的同時實現高度復用。