泛型方法讓您能編寫「型別參數化」的演算法,在編譯期保持型別安全,執行期避免裝箱/拆箱與轉型開銷。本文從基本語法出發,涵蓋型別推斷、類別層級型別參數存取、約束條件、多載規則等核心概念,並附上實用程式碼範例。
在方法名稱後加上 <T> 宣告型別參數:
static void Swap<T>(ref T lhs, ref T rhs)
{
T temp = lhs;
lhs = rhs;
rhs = temp;
}
public static void TestSwap()
{
int a = 1, b = 2;
Swap<int>(ref a, ref b); // 明確指定 T 為 int
Console.WriteLine($"{a} {b}"); // 輸出: 2 1
}
編譯器可根據實際引數推斷型別參數,允許省略 <int>:
Swap(ref a, ref b); // 編譯器推斷 T 為 int
推斷規則:
class SampleClass<T>
{
// 非泛型方法,直接使用類別的 T
void Swap(ref T lhs, ref T rhs) { /* ... */ }
}
若方法自行宣告同名型別參數,會隱藏外層類別的同名參數,編譯器發出警告:
class GenericList<T>
{
// 警告 CS0693: 方法的 T 隱藏了類別的 T
void SampleMethod<T>() { }
}
class GenericList2<T>
{
// 改用不同名稱 U,無警告
void SampleMethod<U>() { }
}
where 子句限制型別參數必須滿足特定條件,啟用更專業操作(如介面方法呼叫、比較、建構函式呼叫等)。
void SwapIfGreater<T>(ref T lhs, ref T rhs) where T : IComparable<T>
{
if (lhs.CompareTo(rhs) > 0)
{
T temp = lhs;
lhs = rhs;
rhs = temp;
}
}
現在呼叫端傳入的型別必須實作 IComparable<T>,否則編譯錯誤。常用約束關鍵字:
| 約束 | 說明 |
|---|---|
where T : class | 參考型別(類別、介面、委派等) |
where T : struct | 實值型別(排除 Nullable) |
where T : new() | 有無參數建構函式 |
where T : BaseClass | 繼承自指定基底類別 |
where T : Interface | 實作指定介面 |
where T : notnull | 不可為 null 的型別 (C# 8.0+) |
where T : unmanaged | 非受控型別 (C# 7.3+) |
where T : default | 覆寫繼承而來的約束 (C# 9.0+) |
可組合多個約束:where T : class, IComparable<T>, new()
泛型方法可與非泛型方法、其他泛型方法共存構成多載群:
void DoWork() { } // 非泛型
void DoWork<T>() { } // 1 個型別參數
void DoWork<T, U>() { } // 2 個型別參數
多載解析優先序:
DoWork(int) 勝過 DoWork<T>(T))。public static class EnumerableExtensions
{
// 擴充方法 + 泛型 + 約束
public static T MaxBy<T, TKey>(this IEnumerable<T> source,
Func<T, TKey> selector)
where TKey : IComparable<TKey>
{
using var enumerator = source.GetEnumerator();
if (!enumerator.MoveNext())
throw new InvalidOperationException("序列為空");
T maxItem = enumerator.Current;
TKey maxKey = selector(maxItem);
while (enumerator.MoveNext())
{
TKey currentKey = selector(enumerator.Current);
if (currentKey.CompareTo(maxKey) > 0)
{
maxKey = currentKey;
maxItem = enumerator.Current;
}
}
return maxItem;
}
}
// 使用範例
var people = new[]
{
new { Name = "Alice", Age = 30 },
new { Name = "Bob", Age = 25 },
new { Name = "Charlie", Age = 35 }
};
var oldest = people.MaxBy(p => p.Age); // 型別推斷:T=匿名型別, TKey=int
Console.WriteLine($"最年長: {oldest.Name} ({oldest.Age} 歲)");
T、TKey、TValue 等具體名稱;多個參數用 T1、T2 或語意名稱。object 或非泛型版本。static class Cache<T> 可實現型別專屬快取。default(T) 或 default (C# 7.1+) 取得型別預設值。泛型方法是 C# 型別系統威力的核心展現。掌握型別推斷、約束、多載解析規則,能讓您撰寫出既彈性又安全、效能優異的可重用程式碼。從簡單的 Swap<T> 到 LINQ 風格的擴充方法,泛型無處不在,值得深入鑽研。