where (泛型類型條件約束) (C# 參考)

泛型定義中的 where 子句指定型別上的條件約束,以用來作為泛型型別、方法、委派或本機函式中型別參數的引數。 條件約束可以指定介面、基底類別或需要作為參考、值或非受控型別的泛型型別。 條件約束會宣告型別引數必須具備的功能,且必須放在任何宣告的基底類別或實作的介面之後。

例如,您可以宣告泛型類別 AGenericClass,讓型別參數 T 實作 IComparable<T> 介面:

public class AGenericClass<T> where T : IComparable<T> { }

注意

如需查詢運算式中 where 子句的詳細資訊,請參閱 where 子句

where 子句也可以包含基底類別條件約束。 基底類別條件約束指出要用作該泛型型別之型別引數的型別,具有作為基底類別的指定類別,或即該基底類別。 如果使用基底類別條件約束,則它必須出現在該型別參數的任何其他條件約束之前。 某些型別不允許作為基底類別條件約束:ObjectArrayValueType。 下列範例會顯示現在可以指定為基底類別的型別:

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 子句可以指定型別是 classstructstruct 條件約束不需要指定 System.ValueType 的基底類別條件約束。 System.ValueType 型別不能用作基底類別條件約束。 下列範例顯示 classstruct 條件約束:

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? 的意思可能會模糊。 如果 TstructT? 會與 System.Nullable<T> 相同。 但如果 T 為參考型別,T? 則表示 null 為有效的值。 模糊是因為覆寫方法不能包含條件約束。 新的 default 條件約束會解決此模糊情況。 您會在基底類別或介面宣告方法的兩個多載,其中一個指定 struct 條件約束,另一個沒有套用 structclass 條件約束時,加以新增:

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 條件約束不能與 classstruct 條件約束合併使用。 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 子句中的最後。 new() 條件約束不能與 structunmanaged 條件約束合併使用。 所有滿足這些條件約束的型別都必須具有可存取的無參數建構函式,讓 new() 條件約束成為備援。

若有多個類型參數,請對每個類型參數使用一個 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# 語法及用法的限定來源。

另請參閱