1. 程式人生 > 程式設計 >詳解C#泛型的型別引數約束

詳解C#泛型的型別引數約束

常用約束

約束告知編譯器型別引數必須具備的功能。 在沒有任何約束的情況下,型別引數可以是任何型別。 編譯器只能假定 System.Object 的成員,它是任何 .NET 型別的最終基類。 如果客戶端程式碼使用不滿足約束的型別,編譯器將發出錯誤。 通過使用 where 上下文關鍵字指定約束。 下表列出了七種型別的約束:

約束 描述
where T : struct 型別引數必須是不可為 null 的值型別。 有關可為 null 的值型別的資訊,請參閱可為 null 的值型別。 由於所有值型別都具有可訪問的無引數建構函式,因此 struct 約束表示 new() 約束,並且不能與 new() 約束結合使用。 struct 約束也不能與 unmanaged 約束結合使用。
where T : class 型別引數必須是引用型別。此約束還應用於任何類、介面、委託或陣列型別。 在 C#8.0 或更高版本中的可為 null 上下文中,T 必須是不可為 null 的引用型別。
where T : class? 型別引數必須是可為 null 或不可為 null 的引用型別。 此約束還應用於任何類、介面、委託或陣列型別。
where T : notnull 型別引數必須是不可為 null 的型別。 引數可以是 C# 8.0 或更高版本中的不可為 null 的引用型別,也可以是不可為 null 的值型別。
where T : unmanaged 型別引數必須是不可為 null 的非託管型別。unmanaged 約束表示 struct 約束,且不能與 struct 約束或 new() 約束結合使用。
where T : new() 型別引數必須具有公共無引數建構函式。與其他約束一起使用時,new() 約束必須最後指定。 new() 約束不能與 struct 和 unmanaged 約束結合使用。
where T : <base class name> 型別引數必須是指定的基類或派生自指定的基類。 在 C# 8.0 及更高版本中的可為 null 上下文中,T 必須是從指定基類派生的不可為 null 的引用型別。
where T : <base class name>? 型別引數必須是指定的基類或派生自指定的基類。在 C# 8.0 及更高版本中的可為 null 上下文中,T 可以是從指定基類派生的可為 null 或不可為 null 的型別。
where T : <interface name> 型別引數必須是指定的介面或實現指定的介面。可指定多個介面約束。 約束介面也可以是泛型。在 C# 8.0 及更高版本中的可為 null 上下文中,T 必須是實現指定介面的不可為 null 的型別。
where T : <interface name>? 型別引數必須是指定的介面或實現指定的介面。可指定多個介面約束。約束介面也可以是泛型。在 C# 8.0 中的可為 null 上下文中,T 可以是可為 null 的引用型別、不可為 null 的引用型別或值型別。T 不能是可為 null 的值型別。
where T : U 為 T 提供的型別引數必須是為 U 提供的引數或派生自為 U 提供的引數。在可為 null 的上下文中,如果 U 是不可為 null 的引用型別,T 必須是不可為 null 的引用型別。如果 U 是可為 null 的引用型別,則 T 可以是可為 null 的引用型別,也可以是不可為 null 的引用型別。

最常用的泛型約束為where T : struct、where T : class、where T : new()。

約束多個引數

可以對多個引數應用多個約束,對一個引數應用多個約束,如下例所示:

class Base { }
class Test<T,U>
  where U : struct
  where T : Base,new()
{ }

對型別引數使用 == 和 != 運算子

在應用 where T : class 約束時,請避免對型別引數使用 == 和 != 運算子。編譯器只知道 T 在編譯時是引用型別,並且必須使用對所有引用型別都有效的預設運算子。

如果必須測試值相等性,建議同時應用 where T : IEquatable 或 where T : IComparable 約束,並在用於構造泛型類的任何類中實現該介面。

參考文章

型別引數的約束(C# 程式設計指南)——Microsoft

以上就是詳解C#泛型的型別引數約束的詳細內容,更多關於C#泛型的型別引數約束的資料請關注我們其它相關文章!