where (restrição de tipo genérico) (Referência C#)

A where cláusula em uma definição genérica especifica restrições nos tipos que são usados como argumentos para parâmetros de tipo em um tipo genérico, método, delegado ou função local. As restrições podem especificar interfaces, classes base ou exigir que um tipo genérico seja uma referência, valor ou tipo não gerenciado. Eles declaram recursos que o argumento type deve ter e devem ser colocados após qualquer classe base declarada ou interfaces implementadas.

Por exemplo, você pode declarar uma classe genérica, AGenericClass, de modo que o parâmetro T type implemente a IComparable<T> interface:

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

Nota

Para obter mais informações sobre a cláusula where em uma expressão de consulta, consulte a cláusula where.

A where cláusula também pode incluir uma restrição de classe base. A restrição de classe base afirma que um tipo a ser usado como um argumento de tipo para esse tipo genérico tem a classe especificada como uma classe base ou é essa classe base. Se a restrição de classe base for usada, ela deverá aparecer antes de quaisquer outras restrições nesse parâmetro de tipo. Alguns tipos não são permitidos como uma restrição de classe base: Object, Arraye ValueType. O exemplo a seguir mostra os tipos que agora podem ser especificados como uma classe base:

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

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

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

Em um contexto anulável, a anulabilidade do tipo de classe base é imposta. Se a classe base for não anulável (por exemplo Base), o argumento type deverá ser não anulável. Se a classe base for anulável (por exemplo Base?), o argumento type pode ser um tipo de referência anulável ou não anulável. O compilador emite um aviso se o argumento type for um tipo de referência anulável quando a classe base não for anulável.

A where cláusula pode especificar que o tipo é um class ou um struct. A struct restrição elimina a necessidade de especificar uma restrição de classe base de System.ValueType. O System.ValueType tipo não pode ser usado como uma restrição de classe base. O exemplo a seguir mostra as class restrições e struct :

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

Em um contexto anulável, a class restrição requer que um tipo seja um tipo de referência não anulável. Para permitir tipos de referência anuláveis, use a class? restrição, que permite tipos de referência anuláveis e não anuláveis.

A where cláusula pode incluir a notnull restrição. A notnull restrição limita o parâmetro type a tipos não anuláveis. O tipo pode ser um tipo de valor ou um tipo de referência não anulável. A notnull restrição está disponível para código compilado em um nullable enable contexto. Ao contrário de outras restrições, se um argumento type viola a notnull restrição, o compilador gera um aviso em vez de um erro. Os avisos são gerados apenas num nullable enable contexto.

A adição de tipos de referência anuláveis introduz uma ambiguidade potencial no significado de T? métodos genéricos. Se T é um struct, T? é o mesmo que System.Nullable<T>. No entanto, se T é um tipo de referência, T? significa que null é um valor válido. A ambiguidade surge porque os métodos predominantes não podem incluir restrições. O novo default constrangimento resolve esta ambiguidade. Você o adiciona quando uma classe base ou interface declara duas sobrecargas de um método, uma que especifica a struct restrição e outra que não tem a struct restrição ou class aplicada:

public abstract class B
{
    public void M<T>(T? item) where T : struct { }
    public abstract void M<T>(T? item);

}

Você usa a default restrição para especificar que sua classe derivada substitui o método sem a restrição em sua classe derivada ou implementação de interface explícita. Só é válido em métodos que substituem métodos base ou implementações de interface explícitas:

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 { }
}

Importante

As declarações genéricas que incluem a notnull restrição podem ser usadas em um contexto nulo esquecido, mas o compilador não impõe a restrição.

#nullable enable
    class NotNullContainer<T>
        where T : notnull
    {
    }
#nullable restore

A where cláusula também pode incluir uma unmanaged restrição. A unmanaged restrição limita o parâmetro type a tipos conhecidos como tipos não gerenciados. A unmanaged restrição facilita a escrita de código de interoperabilidade de baixo nível em C#. Essa restrição permite rotinas reutilizáveis em todos os tipos não gerenciados. A unmanaged restrição não pode ser combinada com a class restrição ou struct . A unmanaged restrição impõe que o tipo deve ser um struct:

class UnManagedWrapper<T>
    where T : unmanaged
{ }

A where cláusula também pode incluir uma restrição de construtor, new(). Essa restrição torna possível criar uma instância de um parâmetro type usando o new operador. A restrição new() permite que o compilador saiba que qualquer argumento de tipo fornecido deve ter um construtor sem parâmetros acessível. Por exemplo:

public class MyGenericClass<T> where T : IComparable<T>, new()
{
    // The following line is not possible without new() constraint:
    T item = new T();
}

A new() restrição aparece em último lugar na cláusula, a where menos que seja seguida pela allows ref struct anti-restrição. A new() restrição não pode ser combinada com as struct restrições ou unmanaged . Todos os tipos que satisfazem essas restrições devem ter um construtor sem parâmetros acessível, tornando a new() restrição redundante.

Esta anti-restrição declara que o argumento type for T pode ser um ref struct tipo. Por exemplo:

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)
    {

    }
}

O tipo ou método genérico deve obedecer às regras de segurança ref para qualquer instância de T porque pode ser um ref struct. A allows ref struct cláusula não pode ser combinada com a class restrição or class? . O allows ref struct anti-restrição deve seguir todas as restrições para esse argumento de tipo.

Com vários parâmetros de tipo, use uma where cláusula para cada parâmetro de tipo, por exemplo:

public interface IMyInterface { }

namespace CodeExample
{
    class Dictionary<TKey, TVal>
        where TKey : IComparable<TKey>
        where TVal : IMyInterface
    {
        public void Add(TKey key, TVal val) { }
    }
}

Você também pode anexar restrições a parâmetros de tipo de métodos genéricos, conforme mostrado no exemplo a seguir:

public void MyMethod<T>(T t) where T : IMyInterface { }

Observe que a sintaxe para descrever restrições de parâmetros de tipo em delegados é a mesma dos métodos:

delegate T MyDelegate<T>() where T : new();

Para obter informações sobre delegados genéricos, consulte Delegados genéricos.

Para obter detalhes sobre a sintaxe e o uso de restrições, consulte Restrições em parâmetros de tipo.

Especificação da linguagem C#

Para obter mais informações, consulte a Especificação da linguagem C#. A especificação da linguagem é a fonte definitiva para a sintaxe e o uso do C#.

Consulte também