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.

Предложение 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.

Предложение 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#. Это ограничение включает подпрограммы с возможностью повторного использования для всех неуправляемых типов. Ограничение 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 последним. Ограничение new() не может использоваться с ограничениями struct или unmanaged. Все типы, удовлетворяющие этим ограничениям, должны иметь доступ к конструктору без параметров, поэтому ограничение 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#.

См. также