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#.