variable type multiple method generic example create clase c# class generics generic-constraints

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.