c# - variable - ¿Cómo puedo obtener el número de enumeraciones como una constante?
que es class en c# (5)
A partir del número total de elementos definidos en una enumeración , veo que puedo obtener el número de enumeraciones usando:
Enum.GetNames(typeof(Item.Type)).Length;
¡Funciona genial!
Pero, necesito este número como un número constante, para que pueda usarlo en la función [Range(int, int)]
Unity.
private const int constEnumCount = Enum.GetNames(typeof(Item.Type)).Length;
Lo anterior no funciona, porque el recuento de enum no es un número constante, por lo que no puedo asignarlo a una variable constante.
¿Cómo puedo obtener el número de enumeraciones como una constante?
Desafortunadamente, esto no es posible de ninguna manera significativa debido a limitaciones técnicas:
-
[Range(int,int)]
es un atributo, y toda la información proporcionada a un atributo tiene que ser un const - La única forma verdaderamente a prueba de balas para obtener el número de valores en una enumeración es usar
Enum.GetValues(typeof(MyEnum)).Length
oEnum.GetNames(typeof(MyEnum)).Length
, que son reflejos de tiempo de ejecución.
Sin embargo, hay hacks que pueden funcionar. Por ejemplo, el valor de una enumeración se puede convertir a un número entero. Mientras nadie defina explícitamente los valores de sus enumeraciones, puede usar el último elemento de su enumeración de la siguiente manera:
[Range(0,(int)MyEnum.LastValue)]
public void BlahBlahBlah() {}
Sin embargo, sepa que tan pronto como alguien agregue una nueva entrada a la enumeración después de la que esté usando o reordene los elementos en la enumeración, su código se romperá impredeciblemente y no se comportará como usted desea.
Es triste, pero el compilador de C # no es lo suficientemente inteligente como para hacer cálculos simples en el compilador como los compiladores de Java, C y C ++. Así que incluso el ejemplo que di solo funcionaría realmente si el LastValue
no se usara para nada más que para marcar el último elemento en la enumeración. Reduce la complejidad del compilador de C #, que también mejora enormemente la velocidad de compilación para su aplicación. Como tal, hay algunas compensaciones que el equipo C # y CLR tomaron para mejorar su experiencia en otros lugares.
No es posible obtener la cantidad de enumeraciones como const
. Enum.GetNames
utiliza la reflexión para obtener estas cosas, y eso es inherentemente una operación en tiempo de ejecución. Por lo tanto, no se puede conocer en tiempo de compilación, que es un requisito para ser const
.
No puede asignar / crear un const
en tiempo de ejecución. Eso es lo que estás tratando de hacer. Un const
debe evaluarse completamente en tiempo de compilación , no en tiempo de ejecución.
No estoy seguro acerca de la Unidad (y por qué requiere una const
) , pero buscaría usar readonly
private readonly int constEnumCount = Enum.GetNames(typeof(Item.Type)).Length;
Suponiendo que necesita un const
porque está tratando de especificar los valores para un Atributo (¿ este ?), Entonces no tiene suerte.
Tus opciones son:
- Hardcode el recuento en la declaración de atributo o como un
const
sí mismo y tenga cuidado de mantener el recuento en sincronización con la definiciónenum
- Utilice PostSharp u otro marco orientado a aspectos para insertar el atributo por usted. Nunca he hecho esto yo solo pero parece posible ( ¿Cómo inyectar un atributo usando un atributo PostSharp? )
Probablemente también puedas arreglártelas para esto con plantillas T4, pero eso se volvería excesivamente kludgy.
Si fuera yo, a menos que esté hablando pero con cientos de enumeraciones diferentes, codificaré la longitud y tal vez agregue un Assert donde lo necesite.
// WARNING - if you add members update impacted Range attributes
public enum MyEnum
{
foo,
bar,
baz
}
[Range(0, 3)]
class Test
{
public Test()
{
int enumCount = Enum.GetNames(typeof(MyEnum)).Length;
int rangeMax = GetType().GetCustomAttributes(typeof(Range), false).OfType<Range>().First().Max;
Debug.Assert(enumCount == rangeMax);
}
static void Main(string[] args)
{
var test = new Test();
}
}
En C #, un const
se define como constante, es decir, el valor (¡no el cálculo que lo produjo!) Está directamente integrado en el conjunto compilado.
Para evitar que haga algo problemático, el compilador no le permite asignar el resultado de un cálculo a una const. Para entender por qué, imagina que fuera posible:
A.dll
public enum Foo { A, B }
B.dll
public const NumberOfFoos = Enum.GetNames(typeof(Foo)).Length;
// Compiles to:
B.dll
public const NumberOfFoos = 2;
Ahora cambia A.dll:
A.dll
public enum Foo { A, B, C, D }
B.dll
public const NumberOfFoos = 2; // Oh no!
Por lo tanto, necesitaría recompilar B.dll para obtener el valor correcto.
Se pone peor: imagine que tenía un ensamblado C.dll, que usa B.NumberOfFoos. El valor 2 se incrustó directamente en C.dll, por lo que también necesitaría ser recompilado, después de B.dll, para obtener el valor correcto. Por lo tanto (pozo de éxito), C # no te permite hacer esa asignación a un const.