Constraints on Type Parameters (C# Programming Guide)
Quando você define uma classe genérico, você pode aplicar restrições para os tipos de tipos que código de cliente pode usar para argumentos Tipo quando ele instancia sua classe. Se o código de cliente tenta instanciar sua classe usando um tipo que não é permitido por uma restrição, o resultado é um erro de tempo de compilação. Essas restrições são chamadas restrições. Restrições são especificadas usando o where palavra-chave contextual. A tabela a seguir lista os seis tipos de restrições :
Restrição |
Descrição |
---|---|
onde t: struct |
O argumento Tipo deve ser um tipo de valor. Qualquer tipo, com exceção do valor Nullable podem ser especificados. Consulte Usando tipos anuláveis (C# guia de programação) para obter mais informações. |
where : classe |
O argumento de tipo deve ser um tipo de referência; Isso se aplica também a qualquer classe, interface, representante ou tipo de matriz. |
where : New() T |
O argumento Tipo deve ter um construtor sem-parâmetros público. Quando usado em conjunto com outras restrições, o new() restrição deve ser especificado por último. |
where : <base class name> |
O argumento Tipo deve ser ou derivar de classe base especificada. |
where : <interface name> |
O argumento Tipo deve ser ou implementam a interface especificada. Várias restrições interface podem ser especificadas. A interface restrições também pode ser genérica. |
where : U |
O argumento do tipo fornecido para t deve ser ou derivar o argumento fornecido para u. |
Por que usar restrições
Se você desejar examinar um item em uma lista genérica para determinar se é válido ou compará-lo com algum outro item, o compilador deve ter alguns garante que o operador ou a chamada do método terá suporte por qualquer argumento de tipo pode ser especificado pelo código do cliente. ESTA GARANTIA é obtida ao aplicar uma ou mais restrições para a definição de classe genérico. Por exemplo, a restrição classe base informa o compilador que somente objetos desse tipo ou derivado desse tipo será usado como argumentos Tipo. Depois que o compilador não tem essa garantia, pode permitir que os métodos desse tipo a ser chamado na classe genérica. Restrições são aplicadas usando a palavra-chave contextual where. O exemplo de código a seguir demonstra a funcionalidade que podemos adicionar o GenericList<T> classe (em Introduction to Generics (C# Programming Guide)), aplicando uma base de classe restrição.
public class Employee
{
private string name;
private int id;
public Employee(string s, int i)
{
name = s;
id = i;
}
public string Name
{
get { return name; }
set { name = value; }
}
public int ID
{
get { return id; }
set { id = value; }
}
}
public class GenericList<T> where T : Employee
{
private class Node
{
private Node next;
private T data;
public Node(T t)
{
next = null;
data = t;
}
public Node Next
{
get { return next; }
set { next = value; }
}
public T Data
{
get { return data; }
set { data = value; }
}
}
private Node head;
public GenericList() //constructor
{
head = null;
}
public void AddHead(T t)
{
Node n = new Node(t);
n.Next = head;
head = n;
}
public IEnumerator<T> GetEnumerator()
{
Node current = head;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
public T FindFirstOccurrence(string s)
{
Node current = head;
T t = null;
while (current != null)
{
//The constraint enables access to the Name property.
if (current.Data.Name == s)
{
t = current.Data;
break;
}
else
{
current = current.Next;
}
}
return t;
}
}
A restrição permite que a classe genérica usar o Employee.Name propriedade porque todos os itens do tipo t são garantidos como um Employee objeto ou um objeto que herda de Employee.
Várias restrições podem ser aplicadas para o mesmo parâmetro, tipo e as restrições próprios podem ser tipos genéricos, da seguinte forma:
class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
// ...
}
Restringindo o parâmetro de tipo, você aumentar o número de operações permitidas e chamadas de método aos quais o tipo de restrições e todos os tipos na sua hierarquia de herança. Portanto, quando você cria classes genéricas ou métodos, se você irá executar qualquer operação nos membros genéricos, além de atribuição simples ou chamar quaisquer métodos não suportados pelo System.Object, você terá que aplicar restrições para o parâmetro de tipo.
Ao aplicar o where T : class restrição, evitar a == e != operadores no parâmetro de tipo porque esses operadores testará para identidade de referência apenas, não para valor de igualdade. Isso é o caso mesmo se esses operadores são sobrecarregados em um tipo que é usado como um argumento. O código a seguir ilustra este ponto; o resultado é false mesmo que o String sobrecargas de classe de == operador.
public static void OpTest<T>(T s, T t) where T : class
{
System.Console.WriteLine(s == t);
}
static void Main()
{
string s1 = "target";
System.Text.StringBuilder sb = new System.Text.StringBuilder("target");
string s2 = sb.ToString();
OpTest<string>(s1, s2);
}
A razão para esse comportamento é que, ao tempo de compilação, o compilador somente sabe que T é um tipo de referência, e portanto deve utilizar os operadores padrão que são válidos para todos os tipos de referência. Se você deve testar a igualdade do valor, a maneira recomendada é também se aplicam a where T : IComparable<T> restrição e implementar a interface do que em qualquer classe que será usado para construir a classe de genéricos.
A restrição de vários parâmetros
Você pode aplicar restrições para vários parâmetros e várias restrições para um único parâmetro, conforme mostrado no exemplo a seguir:
class Base { }
class Test<T, U>
where U : struct
where T : Base, new() { }
Unbounded parâmetros tipo
Parâmetros tipo que têm sem restrições, como T na classe SampleClass<T>{} pública, são chamados parâmetros tipo unbounded. Parâmetros tipo unbounded ter as seguintes regras:
O != e == operadores não podem ser usados porque não há nenhuma garantia de que o argumento do tipo concreto suportará esses operadores.
Pode ser convertidos de e para System.Object ou explicitamente convertido para o tipo de interface.
Você pode comparar e Nulo. Se um parâmetro não vinculado é comparado com null, a comparação sempre retornará false se o argumento de tipo é um tipo de valor.
Parâmetros de tipo como restrições
O uso de um parâmetro de tipo genérico como uma restrição é útil quando uma função de membro com o seu próprio tipo de parâmetro tem que restringir o parâmetro para o parâmetro de tipo do tipo recipiente, como mostrado no exemplo a seguir:
class List<T>
{
void Add<U>(List<U> items) where U : T {/*...*/}
}
No exemplo anterior, T é uma restrição de tipo no contexto da Add método e no contexto de um parâmetro de tipo não vinculado a List classe.
Parâmetros de tipo também podem ser usados como restrições de definições de classe genérica. Observe que o parâmetro de tipo deve ser declarado dentro de colchetes junto com outros parâmetros de tipo:
//Type parameter V is used as a type constraint.
public class SampleClass<T, U, V> where T : V { }
A utilidade dos parâmetros de tipo como restrições de classes genéricas é muito limitada porque o compilador pode assumir nada sobre o parâmetro de tipo, exceto que ela é derivada de System.Object. Use os parâmetros de tipo como restrições de classes genéricas em cenários nos quais você deseja impor um relacionamento de herança entre dois parâmetros de tipo.
Consulte também
Referência
Introduction to Generics (C# Programming Guide)
Generic Classes (C# Programming Guide)