Depois de não conseguir obter algo como o seguinte para compilar:
public class Gen<T> where T : System.Array
{
}
com o erro
Uma restrição não pode ser uma classe especial `System.Array '
Comecei a me perguntar, o que exatamente é uma "aula especial"?
Muitas vezes as pessoas parecem obter o mesmo tipo de erro quando especificam System.Enum
em uma restrição genérica. Eu tenho os mesmos resultados com System.Object
, System.Delegate
, System.MulticastDelegate
e System.ValueType
também.
Existem mais deles? Não consigo encontrar nenhuma informação sobre "classes especiais" em C #.
Além disso, o que há de tão especial nessas classes que não podemos usá-las como uma restrição de tipo genérico?
c#
class
generics
generic-constraints
Mints97
fonte
fonte
System.Object
é uma "classe especial", pois isso é válido:, mas ainda é uma "classe especial".public class X : System.Object { }
System.Object
Respostas:
Do código-fonte Roslyn, parece uma lista de tipos codificados:
Fonte: Binder_Constraints.cs IsValidConstraintType
Encontrei usando uma pesquisa do GitHub: "Uma restrição não pode ser uma classe especial"
fonte
CS0702
.object
), ou pelo menos tem algo a ver com isso. Tambémwhere T : Array
permitiria passar Ensaio como T, o que provavelmente não é o que a maioria das pessoas deseja.Encontrei um comentário de Jon Skeet de 2008 sobre uma questão semelhante: Por que a
System.Enum
restrição não é suportada.Eu sei que isso é um pouco fora do assunto , mas ele perguntou a Eric Lippert (a equipe C #) sobre isso e eles deram esta resposta:
fonte
De acordo com o MSDN , é uma lista estática de classes:
Erro do compilador CS0702
A restrição não pode ser um 'identificador' de classe especial. Os seguintes tipos não podem ser usados como restrições:
fonte
System.MulticastDelegate
na lista?De acordo com a especificação de linguagem C # 4.0 (codificado: [10.1.5] restrições de parâmetro de tipo) diz duas coisas:
Ao definir uma classe genérica, você pode aplicar restrições aos tipos de tipos que o código do cliente pode usar para argumentos de tipo ao instanciar sua classe. Se o código do cliente tentar instanciar sua classe usando um tipo que não é permitido por uma restrição, o resultado será um erro em tempo de compilação. Essas restrições são chamadas de restrições. As restrições são especificadas usando a palavra-chave contextual where. Se você deseja restringir um tipo genérico a um tipo de referência, use: class.
Isso proibirá que o tipo genérico seja um tipo de valor, como int ou uma estrutura etc.
Além disso, a restrição não pode ser um 'identificador' de classe especial. Os seguintes tipos não podem ser usados como restrições:
fonte
Existem certas classes na Estrutura que efetivamente transmitem características especiais para todos os tipos derivados delas, mas não possuem essas características . O próprio CLR não impõe nenhuma proibição contra o uso dessas classes como restrições, mas os tipos genéricos restritos a elas não adquiririam as características não herdadas da mesma forma que os tipos concretos. Os criadores do C # decidiram que, como esse comportamento pode confundir algumas pessoas, e eles deixaram de ver qualquer utilidade nele, eles deveriam proibir essas restrições em vez de permitir que se comportassem como o fazem no CLR.
Se, por exemplo, alguém pudesse escrever
void CopyArray<T>(T dest, T source, int start, int count)
:; seria possível passardest
esource
para métodos que esperam um argumento do tipoSystem.Array
; além disso, seria possível obter a validação em tempo de compilação de quedest
esource
eram os tipos de array compatíveis, mas não seria capaz de acessar elementos do array usando o[]
operador.A incapacidade de usar
Array
como uma restrição é muito fácil de contornar, uma vezvoid CopyArray<T>(T[] dest, T[] source, int start, int count)
que funcionará em quase todas as situações em que o método anterior funcionaria. No entanto, ele tem uma fraqueza: o método anterior funcionaria no cenário em que um ou ambos os argumentos fossem do tipo,System.Array
ao mesmo tempo que rejeitava casos em que os argumentos eram tipos de array incompatíveis; adicionar uma sobrecarga onde ambos os argumentos fossem do tipoSystem.Array
faria o código aceitar os casos adicionais que deveria aceitar, mas também faria com que ele aceitasse erroneamente os casos que não deveria.Acho a decisão de proibir a maioria das restrições especiais cansativa. O único que teria significado semântico zero seria
System.Object
[já que se isso fosse legal como uma restrição, qualquer coisa iria satisfazê-lo].System.ValueType
provavelmente não seria muito útil, uma vez que as referências de tipoValueType
não têm muito em comum com os tipos de valor, mas pode plausivelmente ter algum valor em casos envolvendo Reflexão. AmbosSystem.Enum
eSystem.Delegate
teriam alguns usos reais, mas como os criadores do C # não pensaram neles, eles são proibidos sem um bom motivo.fonte
O seguinte pode ser encontrado no CLR via C # 4ª edição:
Restrições Primárias
Um parâmetro de tipo pode especificar nenhuma restrição primária ou uma restrição primária. Uma restrição primária pode ser um tipo de referência que identifica uma classe que não é selada. Você não pode especificar um dos seguintes tipos de referência especial: System.Object , System.Array , System.Delegate , System.MulticastDelegate , System.ValueType , System.Enum ou System.Void . Ao especificar uma restrição de tipo de referência, você está prometendo ao compilador que um argumento de tipo especificado será do mesmo tipo ou de um tipo derivado do tipo de restrição.
fonte
System.Array
,System.Delegate
,System.MulticastDelegate
,System.Enum
, ouSystem.ValueType
. Além disso, uma declaração de classe genérica não pode ser usadaSystem.Attribute
como uma classe base direta ou indireta.Não creio que exista qualquer definição oficial de "classes especiais" / "tipos especiais".
Você pode pensar neles como tipos, que não podem ser usados com a semântica de tipos "regulares":
PS Eu adicionaria
System.Void
à lista.fonte
System.Void
dá um erro totalmente diferente quando usado como uma restrição genérica =)void
é muito especial. :)System.Array
pode usar métodos comoArray.Copy
mover dados de um para o outro; o código com parâmetros de um tipo restrito aSystem.Delegate
seria capaz de usarDelegate.Combine
neles e converter o resultado para o tipo adequado . Fazer uso efetivo de um tipo conhecido genéricoEnum
usará o Reflection uma vez para cada tipo, mas umHasAnyFlag
método genérico pode ser 10 vezes mais rápido do que um método não genérico.