where (泛型類型條件約束) (C# 參考)
泛型定義中的 where
子句指定型別上的條件約束,以用來作為泛型型別、方法、委派或本機函式中型別參數的引數。 條件約束可以指定介面、基底類別或需要作為參考、值或非受控型別的泛型型別。 條件約束會宣告型別引數必須具備的功能,且必須放在任何宣告的基底類別或實作的介面之後。
例如,您可以宣告泛型類別 AGenericClass
,讓型別參數 T
實作 IComparable<T> 介面:
public class AGenericClass<T> where T : IComparable<T> { }
注意
如需查詢運算式中 where 子句的詳細資訊,請參閱 where 子句。
where
子句也可以包含基底類別條件約束。 基底類別條件約束指出要用作該泛型型別之型別引數的型別,具有作為基底類別的指定類別,或即該基底類別。 如果使用基底類別條件約束,則它必須出現在該型別參數的任何其他條件約束之前。 某些型別不允許作為基底類別條件約束:Object、Array 和 ValueType。 下列範例會顯示現在可以指定為基底類別的型別:
public class UsingEnum<T> where T : System.Enum { }
public class UsingDelegate<T> where T : System.Delegate { }
public class Multicaster<T> where T : System.MulticastDelegate { }
在可為 Null 的內容中,會強制執行基底類別的可 Null 性。 如果基底類別不可為 Null (如 Base
),則型別引數必須不可為 Null。 如果基底類別可為 Null (例如 Base?
),則型別引數可以是可為 Null 或不可為 Null 的參考型別。 基底類別不可為 Null 時,如果型別引數是可為 Null 的參考型別,編譯器會發出警告。
where
子句可以指定型別是 class
或 struct
。
struct
條件約束不需要指定 System.ValueType
的基底類別條件約束。
System.ValueType
型別不能用作基底類別限制式。 下列範例顯示 class
和 struct
條件約束:
class MyClass<T, U>
where T : class
where U : struct
{ }
在可為 Null 的內容中,class
條件約束需要不可為 Null 的參考型別。 若要允許可為 Null 的參考型別,請使用 class?
條件約束,允許可為 Null 和不可為 Null 的參考型別。
where
子句可能包括 notnull
限制式。
notnull
條件約束會將型別參數限制為不可為 Null 的型別。 此型別可以是實值型別或不可為 Null 的參考型別。
notnull
條件約束適用於在 nullable enable
內容中編譯的程式碼。 與其他條件約束不同,如果型別引數違反 notnull
條件約束,編譯器會產生警告,而不是錯誤。 只會在 nullable enable
內容中產生警告。
新增可為 Null 的參考型別,讓泛型方法中 T?
的意思可能會模糊。 如果 T
為 struct
,T?
會與 System.Nullable<T> 相同。 但如果 T
為參考型別,T?
則表示 null
為有效的值。 模糊是因為覆寫方法不能包含條件約束。 新的 default
條件約束會解決此模糊情況。 您會在基底類別或介面宣告方法的兩個多載,其中一個指定 struct
限制式,另一個沒有套用 struct
或 class
限制式,加以新增:
public abstract class B
{
public void M<T>(T? item) where T : struct { }
public abstract void M<T>(T? item);
}
您會使用 default
條件約束,指定衍生類別覆寫方法,不需要衍生類別中的條件約束,或明確介面實作。 只對覆寫基底方法的方法或明確介面實作有效:
public class D : B
{
// Without the "default" constraint, the compiler tries to override the first method in B
public override void M<T>(T? item) where T : default { }
}
重要
包含 notnull
條件約束的泛型宣告可在可為 Null 的遺忘式內容中使用,但編譯器不會強制執行條件約束。
#nullable enable
class NotNullContainer<T>
where T : notnull
{
}
#nullable restore
where
子句也可能包括 unmanaged
限制式。
unmanaged
條件約束會將型別參數限制為稱為「非受控型別」的型別。
unmanaged
條件約束可讓您更輕鬆地使用 C# 撰寫低階 Interop 程式碼。 此條件約束會啟用所有非受控型別的可重複使用常式。
unmanaged
條件約束不能與 class
或 struct
條件約束合併使用。
unmanaged
條件約束會強制型別必須是 struct
:
class UnManagedWrapper<T>
where T : unmanaged
{ }
where
子句也可能包括建構函式限制式 new()
。 該條件約束可讓您使用 new
運算子建立型別參數執行個體。
new() 條件約束可讓編譯器知道提供的任何型別引數都必須有可存取的無參數建構函式。 例如:
public class MyGenericClass<T> where T : IComparable<T>, new()
{
// The following line is not possible without new() constraint:
T item = new T();
}
new()
限制式除非後面接著 where
反限制式,否則其會出現在 allows ref struct
子句中的最後面。
new()
條件約束不能與 struct
或 unmanaged
條件約束合併使用。 所有滿足這些條件約束的型別都必須具有可存取的無參數建構函式,讓 new()
條件約束成為備援。
此反限制式會宣告 T
的型別引數可以是 ref struct
型別。 例如:
public class GenericRefStruct<T> where T : allows ref struct
{
// Scoped is allowed because T might be a ref struct
public void M(scoped T parm)
{
}
}
泛型型別或方法必須遵守 T
的任何執行個體的 ref 安全規則,因為它可能是 ref struct
。
allows ref struct
子句不能與 class
或 class?
限制式合併使用。
allows ref struct
反限制式必須遵循該型別參數的所有限制式。
若有多個類型參數,請對每個類型參數使用一個 where
子句,例如:
public interface IMyInterface { }
namespace CodeExample
{
class Dictionary<TKey, TVal>
where TKey : IComparable<TKey>
where TVal : IMyInterface
{
public void Add(TKey key, TVal val) { }
}
}
您也可以將條件約束附加至泛型方法的型別參數,如下列範例所示︰
public void MyMethod<T>(T t) where T : IMyInterface { }
請注意,描述委派類型參數條件約束的語法與方法的語法相同︰
delegate T MyDelegate<T>() where T : new();
如需泛型委派的資訊,請參閱泛型委派。
如需條件約束語法和用法的詳細資料,請參閱類型參數的條件約束。
C# 語言規格
如需詳細資訊,請參閱<C# 語言規格>。 語言規格是 C# 語法及用法的限定來源。