where (restrição de tipo genérico) (Referência de C#)
A cláusula where
em uma definição genérica especifica restrições sobre os tipos que são usados como argumentos para parâmetros de tipo em um tipo genérico, método, delegado ou função local. 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 de tipo 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 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 declara que um tipo a ser usado como um argumento de tipo para aquele 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 qualquer outra restrição 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 agora podem ser especificados como 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 nulidade do tipo de classe base é imposta. 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 restrição struct
e outra que não tenha a restrição struct
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 restrição default
para especificar que sua classe derivada substitui o método sem a restrição em sua classe derivada ou a 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
Declarações genéricas que incluem a restrição notnull
podem ser usadas 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 torna possível criar uma instância de um parâmetro de tipo usando o operador new
. 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 { }
Observe que a sintaxe para descrever as restrições de parâmetro de tipo em delegados é a mesma que a 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#.