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

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

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

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

注意

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

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

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) ,則類型引數必須不可為 null。 如果基類可為 null (例如 Base?) ,則型別引數可以是可為 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? 。 如果 T 是,則與 struct T? 相同 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 運算子建立型別參數執行個體。 新的 () 條件約束可讓編譯器知道任何提供的型別引數都必須具有可存取的無參數函式。 例如:

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# 語法及用法的限定來源。

另請參閱