這個做法建議多使用泛型方法除非你會把型別參數定義成欄位使用才改用泛型類別。

例如在下面這個常見泛型類別,可以同時兼容 doublestring 類型,看起來沒問題運作也正常但實際上卻有幾個缺點。

public static class Utils<T>
{
	public static T Max(T left, T right) =>
		Comparer<T>.Default.Compare(left, right) < 0 ?
			right : left;
	public static T Min(T left, T right) =>
		Comparer<T>.Default.Compare(left, right) < 0 ?
			left : right;
}

void Main()
{
	double d1 = 4;
	double d2 = 5;
	double max = Utils<double>.Max(d1, d2);
	Console.WriteLine(max);
	
	string foo = "foo";
	string bar = "bar";
	string sMax = Utils<string>.Max(foo, bar);
	Console.WriteLine(sMax);
}

第一個是用起來不方便,例如在第一個 double 的例子中 Utils<double> 明明 d1d2 已經明確定義類型為 double 了, 為什麼不能自己推斷類型呢? Utils<double> 應該可以省略成 Utils 就好,沒必要讓使用者再次聲明類型。

第二個是效率不好,在上面的寫法中都是用 Comparer 來進行比較,也就是說數值類型也是用 Comparer 來比較,但明明數值類型就可以用更快速的 Math.Max() 方法比較。

所以在這種情況下改用泛型方法是比較好的,下面把 Utils 類別的泛型功能移除,然後多加兩個多載方法當型別為 double 的時候直接 用Math 方法比較。

public static class Utils
{
    public static T Max<T>(T left, T right) => 
		Comparer<T>.Default.Compare(left, right) < 0 ? right : left;
    public static double Max(double left, double right) => Math.Max(left, right);
	
	public static T Min<T>(T left, T right) => 
		Comparer<T>.Default.Compare(left, right) < 0 ? left : right;
	public static double Min(double left, double right) => Math.Min(left, right);
}

void Main()
{
	double d1 = 4;
	double d2 = 5;
	double max = Utils.Max(d1, d2);
	Console.WriteLine(max);

	string foo = "foo";
	string bar = "bar";
	string sMax = Utils.Max(foo, bar);
	Console.WriteLine(sMax);
}

這樣不僅使用時不用再傳入類型參數也透過多載的方式讓特定的類型使用專用的方法,假設傳入的是 int 類型也能使用泛型版本的 Max 方法, 我們可以視需求判斷是否要再加入多載的專用方法。

最後有提到有兩種情況必須使用泛型類別:

  1. 當你的 class 內部有保存 type parameters 的狀態的時候(例如集合 List)。
  2. 當你的 class 有實做泛型介面的時候。

例如下面 List<T> 這個例子,內部的 _items 就是保存了 type parameters 的陣列。

 public class List<T> : IList<T>, IList, IReadOnlyList<T>
 {
     private const int DefaultCapacity = 4;

     internal T[] _items; // Do not rename (binary serialization)
     
     ...

Summary

這個例子提出了大部分情況盡量使用泛型方法可以讓方法的使用者方便許多,也可以交由編譯器自行決定要用泛型方法的版本還是用實做類型專用的版本。