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

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

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

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

注意

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

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

public class UsingEnum<T> where T : System.Enum { }

public class UsingDelegate<T> where T : System.Delegate { }

public class Multicaster<T> where T : System.MulticastDelegate { }

在 C# 8.0 和更新版本中可為 Null 的內容中,會強制執行基類類型的可為 Null 性。 例如,如果基類不可為 Null (例如 Base) ,則 type 引數必須是不可為 Null 的。 例如 Base? ,如果基類可為 Null (例如) ,則 type 引數可能是可為 Null 或不可為 Null 的參考型別。 當基類不可為 Null 時,如果類型引數為可為 Null 的參考型別,編譯器就會發出警告。

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

class MyClass<T, U>
    where T : class
    where U : struct
{ }

在 C# 8.0 和更新版本中可為 Null 的內容中 class ,條件約束需要類型為不可為 Null 的參考型別。 若要允許可為 Null 的參考型別,請使用 class? 條件約束,這可同時允許可為 Null 和不可為 Null 的參考型別。

where 句可能包含 notnull 條件約束。 條件 notnull 約束會將類型參數限制為不可為 Null 的類型。 此類型可以是 實值型別 或不可為 Null 的參考型別。 從 notnull C# 8.0 開始,即可在內容中編譯的程式nullable enable代碼使用條件約束。 不同于其他條件約束,如果類型引數違反 notnull 條件約束,編譯器會產生警告,而不是錯誤。 警告只會在內容中 nullable enable 產生。

新增可為 Null 的參考型別,在泛型方法的意義中 T? 引進了潛在的模棱兩可。 如果 TstructT? 則與 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 條件約束不能與 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# 語法及用法的限定來源。

另請參閱