接續上篇,本文涵蓋泛型方法、委托聲明、構造類型的完整定義(開放/封閉、基類介面繼承、成員替換、可訪問性、轉換規則)、Nullable<T>、別名指令與屬性限制。
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,其他修飾符皆可用。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; // 錯誤:約束不計入簽名
}
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
}
Find<int>(arr, test)。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); // 錯誤:模糊
輸入:方法組、實參列表。輸出:每個類型參數的推斷類型實參。
A 對應參數 P 獨立推斷:P 與方法類型參數無關 / 實參為 null / 匿名方法 / 方法組 → 不推斷任何類型(但推斷成功)。P 為陣列類型,A 同秩陣列 → 遞歸用元素類型推斷;秩不同 → 失敗。P 為方法類型參數 MX → 推斷類型為 A。P 為構造類型 → 對 P 中每個類型參數 MX 求唯一 TX 使 A 隱式轉換至 P[MX:=TX]。多解/無解 → 失敗。(約束忽略)params 方法:先試普通形式,失敗再試擴展形式。
F(G<A,B>(7)):> 後緊跟 { } ] > : ; , . ? 之一 → 視為類型實參列表結束;否則視為大於運算子。
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); // 錯誤:無法從返回類型推斷
}
}
它們自身不能有類型參數(雖可在泛型類中使用外部類型參數)。需泛型屬性語義時,請改用泛型方法。
where type_parameter : class_constraint? interface_constraints? constructor_constraint?
class_constraint: class_type (非 sealed、非 System.Array/Delegate/Enum/ValueType、非 object)
interface_constraint: interface_type (不可重複)
constructor_constraint: new()
where 子句,順序任意。where U:T 錯誤)。A 需隱式轉換至約束 C(同一/隱式引用/裝箱/類型參數轉換)。new():A 非 abstract,且有公共無參建構函式(值類型自動滿足)。List<T> : IEnumerable)。new() → 同 object 成員集。Equals/GetHashCode/ToString,透過類型參數調用不裝箱。interface ICounter { void Increment(); }
struct Counter : ICounter {
int value;
void ICounter.Increment() { value++; }
}
static void Test<T>() where T : new(), ICounter {
T x = new T(); x.Increment(); // 修改 x
((ICounter)x).Increment(); // 修改 x 的裝箱副本
}
無約束/任意約束參數 T 擁有:
T → T (identity), T → object (implicit), object → T (explicit), T → interface (explicit), interface → T (implicit)。若約束介面 I:T → I (implicit), T → I 的基介面 (implicit)。
若約束類 C:T → C (implicit reference), C → T (explicit reference), 以及衍生類/實現介面的相互轉換。
用戶定義轉換傳遞規則同樣適用。
null → T 隱式引用轉換始終存在。
陣列協變:T[] → U[] 隱式引用轉換,若 U 為 C/其衍生類/實現介面/約束介面/其基介面。
每個實參為一個類型,可為構造類型或類型參數。Unsafe 代碼中不可為指標類型。必須滿足對應參數約束。
運行時僅存在封閉類型,開放類型純編譯期概念。每個封閉構造類型擁有獨立靜態變量集。相同聲明+相同實參 → 同一類型。
遞歸替換類型參數獲得。例:D<T> : C<T[]>, C<U> : B<IComparable<U>>, B<V> : A → D<int> 基類鏈:C<int[]>, B<IComparable<int[]>>, A, object。
非繼承成員:聲明中類型參數替換為實參(語義替換,非文本)。繼承成員:先確定直接基類構造類型成員,再替換當前類型參數。
C<T1..Tn> 可訪問 ⟺ C 與所有 Ti 均可訪問。最小可訪問性原則。
構造引用類型不支援協變:List<B> 不能轉 List<A>(即使 B:A)。否則可存入 A 實例破壞 List<B> 不變性。同理無 List<B> → List<object> 轉換。
List<A> listA = new List<A>();
List<B> listB = new List<B>();
Collection c1 = listA; // OK
Collection c2 = listB; // OK
List<A> a1 = listB; // 錯誤:無隱式
List<A> a2 = (List<A>)listB; // 錯誤:無顯式
表示可為 null 的值類型。null 隱式轉換至任意 Nullable<T>,結果為默認值(HasValue=false)。等價於 default(Nullable<T>)。
using Alias = ClosedConstructedType; 合法;using Alias = GenericTypeDeclaration; 非法。
開放類型不可用於屬性。封閉構造類型可作屬性實參,但不可作屬性類型名(因 System.Attribute 不可為泛型基類)。
本文完整覆蓋了泛型方法簽名與推斷、委托實例化、約束體系(含裝箱行為)、轉換規則矩陣、構造類型的完整生命週期(基類介面繼承、成員替換、可訪問性、協變禁令)以及 Nullable<T>、別名、屬性等邊界規則。這些是編寫高階泛型庫、理解編譯器錯誤訊息、設計 API 簽名的必備知識。下一篇將專門講解匿名方法規範(第 21 章)。