Después de no poder compilar algo como lo siguiente:
public class Gen<T> where T : System.Array
{
}
con el error
Una restricción no puede ser una clase especial `System.Array '
Empecé a preguntarme, ¿qué es exactamente una "clase especial"?
Las personas a menudo parecen tener el mismo tipo de error cuando especifican System.Enum
en una restricción genérica. Me dieron los mismos resultados con System.Object
, System.Delegate
, System.MulticastDelegate
y System.ValueType
también.
¿Hay más de ellos? No puedo encontrar información sobre "clases especiales" en C #.
Además, ¿qué tienen de especial esas clases que no podemos usarlas como una restricción de tipo genérico?
c#
class
generics
generic-constraints
Mentas97
fuente
fuente
System.Object
que no es una "clase especial", ya que esto es válido:,public class X : System.Object { }
peroSystem.Object
sigue siendo una "clase especial".Respuestas:
Desde el código fuente de Roslyn, parece una lista de tipos codificados:
Fuente: Binder_Constraints.cs IsValidConstraintType
Lo encontré usando una búsqueda de GitHub: "Una restricción no puede ser una clase especial"
fuente
CS0702
.object
), o al menos tiene algo que ver con eso. Tambiénwhere T : Array
permitiría pasar el ensayo como T, que probablemente no es lo que la mayoría de la gente quiere.Encontré un comentario de Jon Skeet de 2008 sobre una pregunta similar: ¿Por qué la
System.Enum
restricción no admite ?Sé que esto está un poco fuera de tema , pero le preguntó a Eric Lippert (el equipo de C #) al respecto y proporcionaron esta respuesta:
fuente
Según MSDN , es una lista estática de clases:
Error del compilador CS0702
La restricción no puede ser un 'identificador' de clase especial Los siguientes tipos no se pueden utilizar como restricciones:
fuente
System.MulticastDelegate
en la lista?Según la especificación del lenguaje C # 4.0 (codificado: [10.1.5] restricciones de parámetro de tipo) dice dos cosas:
Cuando define una clase genérica, puede aplicar restricciones a los tipos de tipos que el código de cliente puede usar para argumentos de tipo cuando crea una instancia de su clase. Si el código del cliente intenta crear una instancia de su clase utilizando un tipo que no está permitido por una restricción, el resultado es un error en tiempo de compilación. Estas restricciones se denominan restricciones. Las restricciones se especifican mediante el uso de la palabra clave contextual where. Si desea restringir un tipo genérico para que sea un tipo de referencia, use: class.
Esto prohibirá que el tipo genérico sea un tipo de valor, como int o una estructura, etc.
Además, Restricción no puede ser un 'identificador' de clase especial. Los siguientes tipos no pueden usarse como restricciones:
fuente
Hay ciertas clases en el Marco que efectivamente transmiten características especiales a todos los tipos derivados de ellas, pero no poseen esas características en sí mismas . El CLR en sí mismo no impone ninguna prohibición contra el uso de esas clases como restricciones, pero los tipos genéricos restringidos a ellas no adquirirían las características no heredadas como lo harían los tipos concretos. Los creadores de C # decidieron que debido a que tal comportamiento podría confundir a algunas personas y no vieron ninguna utilidad en él, deberían prohibir tales restricciones en lugar de permitirles comportarse como lo hacen en CLR.
Si, por ejemplo, a uno se le permitiera escribir
void CopyArray<T>(T dest, T source, int start, int count)
:; uno podría pasardest
yasource
métodos que esperan un argumento de tipoSystem.Array
; Además, se obtendría la validación en tiempo de compilación de quedest
ysource
eran los tipos de matriz compatibles, pero no se podría acceder a los elementos de la matriz utilizando el[]
operador.La incapacidad de usar
Array
como restricción es bastante fácil de evitar, yavoid CopyArray<T>(T[] dest, T[] source, int start, int count)
que funcionará en casi todas las situaciones en las que funcionaría el método anterior. Sin embargo, tiene una debilidad: el método anterior funcionaría en el escenario en el que uno o ambos argumentos fueran de tipoSystem.Array
mientras se rechazan los casos en los que los argumentos son tipos de matrices incompatibles; agregar una sobrecarga donde ambos argumentos fueran de tipoSystem.Array
haría que el código aceptara los casos adicionales que debería aceptar, pero también haría que aceptara erróneamente los casos que no debería.Encuentro molesta la decisión de prohibir la mayoría de las restricciones especiales. El único que tendría un significado semántico cero sería
System.Object
[ya que si eso fuera legal como restricción, cualquier cosa lo satisfaría].System.ValueType
probablemente no sería muy útil, ya que las referencias de tipoValueType
no tienen mucho en común con los tipos de valor, pero podría tener algún valor plausiblemente en casos que involucren Reflection. AmbosSystem.Enum
ySystem.Delegate
tendrían algunos usos reales, pero como los creadores de C # no pensaron en ellos, están prohibidos sin una buena razón.fuente
Lo siguiente se puede encontrar en CLR a través de C # 4th Edition:
Restricciones primarias
Un parámetro de tipo puede especificar cero restricciones primarias o una restricción primaria. Una restricción primaria puede ser un tipo de referencia que identifica una clase que no está sellada. No puede especificar uno de los siguientes tipos de referencia especiales: System.Object , System.Array , System.Delegate , System.MulticastDelegate , System.ValueType , System.Enum o System.Void . Al especificar una restricción de tipo de referencia, le promete al compilador que un argumento de tipo especificado será del mismo tipo o de un tipo derivado del tipo de restricción.
fuente
System.Array
,System.Delegate
,System.MulticastDelegate
,System.Enum
, oSystem.ValueType
. Además, una declaración de clase genérica no se puede utilizarSystem.Attribute
como clase base directa o indirecta.No creo que exista una definición oficial de "clases especiales" / "tipos especiales".
Puede pensar en ellos como tipos, que no se pueden usar con semánticas de tipos "regulares":
PD: lo agregaría
System.Void
a la lista.fuente
System.Void
da un error completamente diferente cuando se usa como una restricción genérica =)void
es muy especial. :)System.Array
podría usar métodos comoArray.Copy
mover datos de uno a otro; el código con parámetros de un tipo restringidoSystem.Delegate
podría usarseDelegate.Combine
en ellos y emitir el resultado al tipo adecuado . Hacer un uso efectivo de un tipo genérico conocidoEnum
será usar Reflection una vez para cada tipo, pero unHasAnyFlag
método genérico puede ser 10 veces más rápido que un método no genérico.