c# - type - ¿Qué es exactamente una "clase especial"?
generic variable c# (7)
Desde el código fuente de Roslyn, parece una lista de tipos codificados:
switch (type.SpecialType)
{
case SpecialType.System_Object:
case SpecialType.System_ValueType:
case SpecialType.System_Enum:
case SpecialType.System_Delegate:
case SpecialType.System_MulticastDelegate:
case SpecialType.System_Array:
// "Constraint cannot be special class ''{0}''"
Error(diagnostics, ErrorCode.ERR_SpecialTypeAsBound, syntax, type);
return false;
}
Fuente:
Binder_Constraints.cs IsValidConstraintType
Lo encontré usando una búsqueda de GitHub:
"Una restricción no puede ser una clase especial"
Después de no poder obtener algo como lo siguiente para compilar:
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.
System.ValueType
los mismos resultados con
System.Object
,
System.Delegate
,
System.MulticastDelegate
y
System.ValueType
también.
¿Hay más de ellos? No puedo encontrar ninguna información sobre "clases especiales" en C #.
Además, ¿qué tienen de especial esas clases que no podemos usarlas como restricción de tipo genérico?
Encontré un comentario de Jon Skeet de 2008 sobre una pregunta similar: ¿Por qué
no se
admite la restricción
System.Enum
?
Sé que esto está un poco fuera de tema , pero le preguntó a Eric Lippert (el equipo de C #) al respecto y le dieron esta respuesta:
En primer lugar, su conjetura es correcta; Las restricciones a las restricciones son, en general, artefactos del lenguaje, no tanto el CLR. (Si hiciéramos estas funciones, habría algunas cosas menores que nos gustaría cambiar en el CLR con respecto a cómo se especifican los tipos enumerables, pero sobre todo esto sería un trabajo de lenguaje).
En segundo lugar, personalmente me encantaría tener restricciones de delegado, restricciones de enumeración y la capacidad de especificar restricciones que son ilegales hoy porque el compilador está tratando de salvarte de ti mismo. (Es decir, hacer que los tipos sellados sean legales como restricciones, etc.)
Sin embargo, debido a restricciones de programación, es probable que no podamos incluir estas características en la próxima versión del idioma.
Hay ciertas clases en el Marco que efectivamente transfieren 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 de la manera en que 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 pudieron ver su utilidad, deberían prohibir tales restricciones en lugar de permitirles comportarse como lo hacen en el CLR.
Si, por ejemplo, se le permite a uno escribir:
void CopyArray<T>(T dest, T source, int start, int count)
;
uno podría pasar
dest
y
source
a métodos que esperan un argumento de tipo
System.Array
;
Además, se obtendría una validación en tiempo de compilación de que
dest
y
source
eran los tipos de matriz compatibles, pero no se podría acceder a los elementos de la matriz utilizando el operador
[]
.
La imposibilidad de usar
Array
como restricción es bastante fácil de
void CopyArray<T>(T[] dest, T[] source, int start, int count)
, ya que
void CopyArray<T>(T[] dest, T[] source, int start, int count)
funcionará en casi todas las situaciones donde el método anterior funcionaría trabajo.
Sin embargo, tiene una debilidad: el método anterior funcionaría en el caso de que uno o ambos argumentos fueran del tipo
System.Array
mientras rechaza los casos en que los argumentos son tipos de matriz incompatibles;
agregando una sobrecarga donde ambos argumentos eran del tipo
System.Array
haría que el código aceptara los casos adicionales que debería aceptar, pero también haría que aceptara erróneamente casos que no debería.
La decisión de prohibir la mayoría de las restricciones especiales me parece molesta.
El único que tendría cero significado semántico 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 tipo
ValueType
realmente no tienen mucho en común con los tipos de valor, pero es posible que tenga algún valor en los casos que involucran Reflexión.
Tanto
System.Enum
como
System.Delegate
tendrían algunos usos reales, pero dado que los creadores de C # no pensaron en ellos, están prohibidos sin ninguna buena razón.
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 está prometiendo al compilador que un argumento de tipo especificado será del mismo tipo o de un tipo derivado del tipo de restricción.
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ántica de tipos "normales":
- no puedes instanciarlos directamente;
- no puede heredar el tipo personalizado de ellos directamente;
- hay algo de magia de compilación para trabajar con ellos (opcionalmente);
- el uso directo de sus instancias al menos inútil (opcionalmente; imagina que has creado genérico arriba, ¿qué código genérico vas a escribir?)
PD:
System.Void
a la lista.
Según MSDN , es una lista estática de clases:
Error de compilador CS0702
La restricción no puede ser un identificador de clase especial. Los siguientes tipos no pueden usarse como restricciones:
- System.Object
- Sistema Array
- System.Delegate
- System.Enum
- System.ValueType.
Según la especificación del lenguaje C # 4.0 (codificado: [10.1.5] restricciones de parámetros de tipo) dice dos cosas:
1] El tipo no debe ser objeto. Debido a que todos los tipos derivan del objeto, dicha restricción no tendría efecto si se permitiera.
2] Si T no tiene restricciones primarias o restricciones de parámetros de tipo, su clase base efectiva es objeto.
Cuando define una clase genérica, puede aplicar restricciones a los tipos de tipos que el código del cliente puede usar para los 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 llaman restricciones. Las restricciones se especifican utilizando la palabra clave contextual where. Si desea restringir un tipo genérico para que sea un tipo de referencia, use: class.
public class Gen<T> where T : class
{
}
Esto prohibirá que el tipo genérico sea un tipo de valor, como int o una estructura, etc.
Además, la restricción no puede ser un "identificador" de clase especial. Los siguientes tipos no pueden utilizarse como restricciones:
- System.Object
- Sistema Array
- System.Delegate
- System.Enum
- System.ValueType.