首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 2.0 泛型規範深度解析(四):泛型方法完整規範——簽名、重載、虛擬、推斷與委托綁定

     編輯:關於C語言

20.6 泛型方法:從聲明到調用的完整生命週期

泛型方法將參數化能力下放至方法層級,配合類型推斷實現極致的調用便利性。本文系統梳理聲明語法、簽名規則、虛擬/重寫機制、調用與推斷算法、語法歧義消解、委托綁定、以及非泛型成員的限制。

20.6 泛型方法聲明

generic_method_declaration:
  attributes_opt  method_modifiers_opt  return_type  member_name
  type_parameter_list  ( formal_parameter_list_opt )
  type_parameter_constraints_clauses_opt  method_body
  • 類型參數列表緊隨方法名(Find<T>(T[] items, Test<T> test))。
  • 作用域:返回類型、參數列表、方法體、約束子句(不含屬性)。
  • 不可為 extern,其他修飾符皆可用。

示例:通用查找器

public delegate bool Test<T>(T item);
public class Finder {
  public static T Find<T>(T[] items, Test<T> test) {
    foreach (T item in items) if (test(item)) return item;
    throw new InvalidOperationException("Item not found");
  }
}

20.6.1 泛型方法簽名

  • 簽名比較忽略約束、忽略類型參數名稱,但計算參數數量與順序。
  • 同一介面/類別中,兩個同名泛型方法若在某種封閉構造類型下產生相同簽名 → 錯誤(約束不參與檢查)。
interface IX {
  T F1<T>(T[] a, int i);  // 錯誤:返回類型與參數名無關,兩者簽名相同
  void F1<U>(U[] a, int i);
  void F2<T>(int x); void F2(int x); // OK:類型參數數量不同
  void F3<T>(T t) where T:A; void F3<T>(T t) where T:B; // 錯誤:約束不計入簽名
}

泛型類別內的泛型方法重載

規則:兩個同名、同類型參數數量的泛型方法,其參數類型在所有可能封閉構造類型下不得產生相同簽名(約束忽略)。

class X<T> {
  void F<U>(T t, U u) { } // 錯誤:X<int>.F<int> 產生兩個 F(int, int)
  void F<U>(U u, T t) { }
}

20.6.2 虛擬泛型方法

  • 支援 abstract/virtual/override。
  • 重寫/實現時,每個類型參數的約束必須完全一致(按左到右位置對應)。
abstract class Base {
  public abstract T F<T,U>(T t, U u);
  public abstract T G<T>(T t) where T: IComparable;
}
class Derived : Base {
  public override X F<X,Y>(X x, Y y) { } // OK:參數名可不同
  public override T G<T>(T t) { }       // 錯誤:缺少 where T:IComparable
}

20.6.3 調用泛型方法:顯式實參 vs 類型推斷

  • 可顯式指定:Find<int>(arr, test)。
  • 省略實參 → 編譯器依類型推斷(20.6.4)推導。
  • 重載決策在類型推斷之後進行。
static void F<T>(int x, T y) { Console.WriteLine("One"); }
static void F<T>(T x, long y) { Console.WriteLine("Two"); }

F<int>(5, 324);   // "One"
F<byte>(5, 324);  // "Two"
F<double>(5, 324); // 錯誤:模糊
F(5, 324);        // "One" (推斷 T=int)
F(5, 324L);       // 錯誤:模糊

20.6.4 類型推斷算法詳解

輸入:方法組、實參列表。輸出:每個類型參數的推斷類型實參。

  1. 實參數量不匹配 → 立即失敗。
  2. 對每個實參 A 對應參數 P 獨立推斷:
  • P 與方法類型參數無關 / 實參為 null / 匿名方法 / 方法組 → 不推斷任何類型(但推斷成功)。
  • P 為陣列類型,A 同秩陣列 → 遞歸用元素類型推斷;秩不同 → 失敗。
  • P 為方法類型參數 MX → 推斷類型為 A。
  • P 為構造類型 → 對 P 中每個類型參數 MX 求唯一 TX 使 A 隱式轉換至 P[MX:=TX]。多解/無解 → 失敗。(約束忽略)
  1. 匯聚所有推斷:必須完整(每參數有推斷)且一致(同參數推斷相同類型)。

params 方法:先試普通形式,失敗再試擴展形式。

20.6.5 語法歧義消解

F(G<A,B>(7)):> 後緊跟 { } ] > : ; , . ? 之一 → 視為類型實參列表結束;否則視為大於運算子。

20.6.6 委托使用泛型方法

  • 委托實例化時確定類型實參(顯式或推斷)。
  • 推斷時僅用委托參數類型,不用返回類型。
delegate int D(string s, int i);
delegate int E();
class X {
  public static T F<T>(string s, T t) { ... }
  public static T G<T>() { ... }
  static void Main() {
    D d1 = new D(F<int>); // 顯式
    D d2 = new D(F);      // 推斷 int
    E e1 = new E(G<int>); // 顯式
    E e2 = new E(G);      // 錯誤:無法從返回類型推斷
  }
}

20.6.7 非泛型屬性/事件/索引子/運算子

它們自身不能有類型參數(雖可在泛型類中使用外部類型參數)。需泛型屬性語義時,請改用泛型方法。

總結:泛型方法設計精要

  1. 簽名唯一性:約束不計入簽名,重載衝突檢查忽略約束,防止運行時歧義。
  2. 虛擬約束一致性:重寫必須逐參數複製約束,保證調用端類型安全。
  3. 推斷優先於重載:推斷失敗的方法直接退出重載候選集,不報錯;最終無候選才報錯。
  4. 推斷核心邏輯:從實參類型 A 到參數類型 P 的結構匹配,陣列遞歸、構造類型解方程、約束不參與。
  5. 委托綁定非對稱:推斷只看參數,不看返回類型;無參委托綁定無參數泛型方法需顯式指定類型實參。
  6. 語法歧義規則:> 後跟特定符號才視為類型列表結束,保證向後兼容。

泛型方法是 C# 類型系統表達力的關鍵延伸。精通其推斷與重載規則,能設計出如 LINQ 擴展方法般既強類型又極致簡潔的 API。下一篇將進入約束系統深度解析(20.7 章)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved