Compartilhar via


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

Em uma definição genérica, use a where cláusula para especificar restrições nos tipos que você usa como argumentos para parâmetros de tipo em um tipo genérico, método, delegado ou função local. Você pode especificar interfaces, classes base ou exigir que um tipo genérico seja um tipo de referência, valor ou não gerenciado. Essas restrições declaram recursos que o argumento de tipo deve ter. Coloque a where cláusula após qualquer classe base declarada ou interfaces implementadas.

A linguagem C# faz referência a documentos da versão mais recentemente lançada da linguagem C#. Ele também contém a documentação inicial para funcionalidades em pré-visualizações públicas para o próximo lançamento do idioma.

A documentação identifica qualquer recurso introduzido pela primeira vez nas três últimas versões do idioma ou nas versões prévias públicas atuais.

Dica

Para descobrir quando um recurso foi introduzido pela primeira vez em C#, consulte o artigo sobre o histórico de versão da linguagem C#.

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

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

Observação

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

A cláusula where também pode incluir uma restrição de classe base. A restrição de classe base indica 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 é aquela classe base. Se você usar a restrição de classe base, ela deverá aparecer antes de quaisquer outras restrições nesse parâmetro de tipo. Alguns tipos não têm permissão como uma restrição de classe base: Object, Array e ValueType. O exemplo a seguir mostra os tipos que você pode especificar 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, o compilador impõe a nulidade do tipo de classe base. Se a classe base não for anulável (por exemplo Base), o argumento de tipo deverá ser não anulável. Se a classe base for anulável (por exemplo, Base?), o argumento type poderá ser um tipo de referência anulável ou não anulável. O compilador emitirá um aviso se o argumento de tipo for um tipo de referência anulável quando a classe base não for anulável.

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

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

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

A cláusula where pode incluir a restrição notnull. A restrição notnull limita o parâmetro de tipo 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 restrição notnull está disponível para código compilado em um nullable enable contexto. Ao contrário de outras restrições, se um argumento de tipo violar a restrição notnull, o compilador gerará um aviso em vez de um erro. Os avisos são gerados apenas em um contexto nullable enable.

A adição de tipos de referência anuláveis introduz uma ambiguidade potencial no significado de T? em métodos genéricos. Se T for um struct, T? é o mesmo que System.Nullable<T>. No entanto, se T for um tipo de referência, T? significa que null é um valor válido. A ambiguidade surge porque os métodos de substituição não podem incluir restrições. A nova restrição default resolve essa ambiguidade. Adicione-o quando uma classe base ou interface declarar duas sobrecargas de um método, uma que especifica a struct restrição e outra que não tenha a restrição ou a structclass restrição aplicada:

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

}

Use 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. Ele 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

Você pode usar declarações genéricas que incluem a notnull restrição em um contexto alheio anulável, mas o compilador não impõe a restrição.

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

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

class UnManagedWrapper<T>
    where T : unmanaged
{ }

A cláusula where também pode incluir uma restrição de construtor, new(). Essa restrição possibilita a criação de uma instância de um parâmetro de tipo usando o new operador. A restrição new() informa o compilador 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 restrição new() aparece por último na cláusula where, a menos que seja seguida pelo allows ref struct anti-restrição. A restrição new() não pode ser combinada às restrições struct ou unmanaged. Todos os tipos que satisfazem as restrições devem ter um construtor sem parâmetros acessível, tornando a restrição new() redundante.

Essa anti-restrição declara que o argumento de tipo para T pode ser um tipo de ref struct. 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 de ref para qualquer instância de T porque pode ser um ref struct. A restrição allows ref struct não pode ser combinada à restrição class ou 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 cláusula where 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, como mostrado no exemplo a seguir:

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

A sintaxe para descrever restrições de parâmetro 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 a 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 de C#.

Confira também