Estoy construyendo una función para extender el Enum.Parse
concepto que
- Permite analizar un valor predeterminado en caso de que no se encuentre un valor Enum
- Es insensible a mayúsculas y minúsculas
Entonces escribí lo siguiente:
public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
if (string.IsNullOrEmpty(value)) return defaultValue;
foreach (T item in Enum.GetValues(typeof(T)))
{
if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
}
return defaultValue;
}
Recibo una restricción de error no puede ser una clase especial System.Enum
.
Es justo, pero hay una solución alternativa para permitir una enumeración genérica, o tendré que imitar la Parse
función y pasar un tipo como un atributo, lo que obliga al requisito de boxeo feo a su código.
EDITAR Todas las sugerencias a continuación han sido muy apreciadas, gracias.
He decidido (he dejado el bucle para mantener la insensibilidad a mayúsculas y minúsculas; estoy usando esto al analizar XML)
public static class EnumUtils
{
public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
{
if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
if (string.IsNullOrEmpty(value)) return defaultValue;
foreach (T item in Enum.GetValues(typeof(T)))
{
if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
}
return defaultValue;
}
}
EDITAR: (16 de febrero de 2015) Julien Lebosquain ha publicado recientemente una solución genérica segura de tipo forzada por el compilador en MSIL o F # a continuación, que bien vale la pena ver, y un voto positivo. Eliminaré esta edición si la solución aparece más arriba en la página.
fuente
Respuestas:
Dado que
Enum
Type implementa laIConvertible
interfaz, una mejor implementación debería ser algo como esto:Esto seguirá permitiendo la implementación de tipos de valor
IConvertible
. Sin embargo, las posibilidades son raras.fuente
¡Esta característica finalmente es compatible con C # 7.3!
El siguiente fragmento (de las muestras de dotnet ) demuestra cómo:
Asegúrese de configurar su versión de idioma en su proyecto C # a la versión 7.3.
Respuesta original a continuación:
Llego tarde al juego, pero lo tomé como un desafío para ver cómo podría hacerse. No es posible en C # (o VB.NET, pero desplácese hacia abajo para F #), pero es posible en MSIL. Escribí esta pequeña cosa ...
Lo que genera una función que se vería así, si fuera válido C #:
Luego con el siguiente código C #:
Desafortunadamente, esto significa tener esta parte de su código escrita en MSIL en lugar de C #, con el único beneficio adicional de que puede restringir este método
System.Enum
. También es una especie de fastidio, porque se compila en un ensamblaje separado. Sin embargo, no significa que tenga que implementarlo de esa manera.Al eliminar la línea
.assembly MyThing{}
e invocar ilasm de la siguiente manera:obtienes un netmodule en lugar de un ensamblaje.
Desafortunadamente, VS2010 (y anterior, obviamente) no admite agregar referencias de netmodule, lo que significa que tendría que dejarlo en 2 ensamblajes separados cuando esté depurando. La única forma en que puede agregarlos como parte de su ensamblado sería ejecutar csc.exe usted mismo utilizando el
/addmodule:{files}
argumento de la línea de comandos. No sería demasiado doloroso en un script de MSBuild. Por supuesto, si eres valiente o estúpido, puedes ejecutar csc tú mismo manualmente cada vez. Y ciertamente se vuelve más complicado ya que múltiples ensambles necesitan acceso a él.Entonces, PUEDE hacerse en .Net. ¿Vale la pena el esfuerzo extra? Um, bueno, supongo que te dejaré decidir sobre eso.
Solución F # como alternativa
Crédito adicional: Resulta que
enum
es posible una restricción genérica en al menos otro lenguaje .NET además de MSIL: F #.Este es más fácil de mantener ya que es un lenguaje bien conocido con soporte completo de Visual Studio IDE, pero aún necesita un proyecto separado en su solución para ello. Sin embargo, naturalmente produce una IL considerablemente diferente (el código es muy diferente) y se basa en la
FSharp.Core
biblioteca, que, al igual que cualquier otra biblioteca externa, debe formar parte de su distribución.Así es como puede usarlo (básicamente lo mismo que la solución MSIL), y para mostrar que falla correctamente en otras estructuras:
fuente
There's no particularly unusual reason why not; we have lots of other things to do, limited budgets, and this one has never made it past the "wouldn't this be nice?" discussion in the language design team.
T
aSystem.Enum
no podría hacer todas las cosas con lasT
que la gente podría esperar, los autores de C # pensaron que también podrían prohibirlo por completo. Considero que la decisión es desafortunada, ya que C # simplemente había ignorado cualquier manejo especial deSystem.Enum
restricciones, habría sido posible escribir unHasAnyFlags<T>(this T it, T other)
método de extensión que fuera de órdenes de magnitud más rápidoEnum.HasFlag(Enum)
y que verificara sus argumentos.C # ≥ 7.3
A partir de C # 7.3 (disponible con Visual Studio 2017 ≥ v15.7), este código ahora es completamente válido:
C # ≤ 7.2
Puede tener una restricción de enumeración forzada del compilador real al abusar de la herencia de restricciones. El siguiente código especifica restricciones a
class
ystruct
a al mismo tiempo:Uso:
Nota: esto se especifica específicamente en la especificación del lenguaje C # 5.0:
fuente
EnumClassUtils<System.Enum>
es suficiente para restringir T a cualquierSystem.Enum
tipo derivado.struct
en laParse
continuación restringe aún más a un verdadero tipo de enumeración. Necesita restringir aEnum
en algún momento. Para hacerlo, tu clase tiene que estar anidada. Ver gist.github.com/MrJul/7da12f5f2d6c69f03d79where TClass : class
gana aquí la restricción?enum DefinitelyNotAnInt : byte { Realize, That, I, Am, Not, An, Int }
enum AlsoNotAnInt : long { Well, Bummer }
Editar
La pregunta ahora ha sido respondida magníficamente por Julien Lebosquain . También me gustaría extender su respuesta con
ignoreCase
,defaultValue
y argumentos opcionales, mientras agregaTryParse
yParseOrDefault
.Ejemplos de uso:
Antiguo
Mis viejas mejoras en la respuesta de Vivek mediante el uso de los comentarios y 'nuevos' desarrollos:
TEnum
para mayor claridad para los usuariosTryParse
manejarignoreCase
con el parámetro existente (introducido en VS2010 / .Net 4)default
valor genérico (introducido en VS2005 / .Net 2)defaultValue
yignoreCase
Resultando en:
fuente
Puede definir un constructor estático para la clase que verificará que el tipo T sea una enumeración y arroje una excepción si no lo es. Este es el método mencionado por Jeffery Richter en su libro CLR a través de C #.
Luego, en el método de análisis, puede usar Enum.Parse (typeof (T), input, true) para convertir de cadena a enum. El último parámetro verdadero es para ignorar el caso de la entrada.
fuente
Enum
T
cuando se ejecutó el constructor. Aunque esto es mucho mejor que esperar un constructor de instancias.También se debe tener en cuenta que, dado que el lanzamiento de C # 7.3 con restricciones Enum se admite de forma inmediata sin tener que realizar comprobaciones adicionales y demás.
Entonces, en adelante, y dado que ha cambiado la versión de idioma de su proyecto a C # 7.3, el siguiente código funcionará perfectamente bien:
En caso de que no sepa cómo cambiar la versión del idioma a C # 7.3, vea la siguiente captura de pantalla:
EDITAR 1 - Versión de Visual Studio requerida y considerando ReSharper
Para que Visual Studio reconozca la nueva sintaxis, necesita al menos la versión 15.7. Puede encontrar eso también mencionado en las notas de la versión de Microsoft, consulte las Notas de la versión de Visual Studio 2017 15.7 . Gracias @MohamedElshawaf por señalar esta pregunta válida.
También tenga en cuenta que en mi caso ReSharper 2018.1 al momento de escribir este EDIT todavía no es compatible con C # 7.3. Al tener ReSharper activado, resalta la restricción Enum como un error que me dice No se puede usar 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'object' como restricción de parámetro de tipo . ReSharper sugiere como una solución rápida para eliminar la restricción 'Enum' del parámetro de tipo T del método
Sin embargo, si desactiva ReSharper temporalmente en Herramientas -> Opciones -> ReSharper Ultimate -> General , verá que la sintaxis está perfectamente bien dado que usa VS 15.7 o superior y C # 7.3 o superior.
fuente
where T : struct, Enum
, para evitar pasarse aSystem.Enum
sí mismo como parámetro de tipo.struct, Enum
. Mi justificación se explica en la respuesta y los comentarios aquí .Modifiqué la muestra por dimarzionista. Esta versión solo funcionará con Enums y no permitirá que las estructuras pasen.
fuente
Traté de mejorar un poco el código:
fuente
defaultValue.ToString("D", System.Globalization.NumberFormatInfo.CurrentInfo)
a pesar de que no sabe qué tipo de enumeración es, solo que el objeto es una enumeración.IsDefined
embargo, la verificación previa con arruinará la insensibilidad de mayúsculas y minúsculas. A diferenciaParse
,IsDefined
no tieneignoreCase
argumento, y MSDN dice que solo coincide con el caso exacto .Tengo requisitos específicos donde requería usar enum con texto asociado con el valor de enum. Por ejemplo, cuando uso enum para especificar el tipo de error, es necesario que describa los detalles del error.
fuente
Espero que esto sea útil:
fuente
return (TValue)Enum.Parse(typeof (TValue), value);
porreturn (TValue)Enum.Parse(typeof (TValue), value, true);
Curiosamente, aparentemente esto es posible en otros idiomas (Managed C ++, IL directamente).
Citar:
Quién sabe
fuente
Esta es mi opinión al respecto. Combinado de las respuestas y MSDN
Fuente MSDN
fuente
TEnum
realidad es un tipo Enum perotext
es una cadena vacía,ArgumentException
aparece el mensaje "TEnum debe ser un tipo Enum" aunque lo sea.Las respuestas existentes son verdaderas a partir de C # <= 7.2. Sin embargo, hay una solicitud de función de lenguaje C # (vinculada a una solicitud de función corefx ) para permitir lo siguiente;
Al momento de escribir, la función está "en discusión" en las reuniones de desarrollo del lenguaje.
EDITAR
Según la información de nawfal , esto se está introduciendo en C # 7.3 .
fuente
Siempre me ha gustado esto (puedes modificarlo según corresponda):
fuente
Me encantó la solución de Christopher Currens usando IL, pero para aquellos que no quieren lidiar con el complicado negocio de incluir MSIL en su proceso de creación, escribí una función similar en C #.
Sin embargo, tenga en cuenta que no puede usar la restricción genérica
where T : Enum
porque Enum es de tipo especial. Por lo tanto, tengo que verificar si el tipo genérico dado es realmente enum.Mi funcion es:
fuente
Encapsulé la solución de Vivek en una clase de utilidad que puede reutilizar. Tenga en cuenta que aún debe definir restricciones de tipo "donde T: struct, IConvertible" en su tipo.
fuente
Creé un método de extensión
to get integer value from enum
eche un vistazo a la implementación del métodoeste es el uso
fuente
Como se indicó en otras respuestas antes; Si bien esto no se puede expresar en el código fuente, en realidad se puede hacer en el nivel IL. La respuesta de @Christopher Currens muestra cómo le va a la IL a eso.
Con Fody s de complementos ExtraConstraints.Fody hay una manera muy simple, con la acumulación de herramientas, para lograr esto. Simplemente agregue sus paquetes nuget (
Fody
,ExtraConstraints.Fody
) a su proyecto y agregue las restricciones de la siguiente manera (Extracto del archivo Léame de ExtraConstraints):y Fody agregará la IL necesaria para que la restricción esté presente. También tenga en cuenta la característica adicional de restringir delegados:
Con respecto a Enums, es posible que también desee tomar nota de la muy interesante Enums.NET .
fuente
Esta es mi implementación. Básicamente, puede configurar cualquier atributo y funciona.
fuente
Si está bien usar el lanzamiento directo después, supongo que puede usar la
System.Enum
clase base en su método, cuando sea necesario. Solo necesita reemplazar los parámetros de tipo cuidadosamente. Entonces la implementación del método sería como:Entonces puedes usarlo como:
fuente
Enum.ToObject()
produciría un resultado más flexible. Además de eso, usted podría hacer las comparaciones de cadenas sin mayúsculas y minúsculas que negar la necesidad de llamarToLower()
Solo para completar, la siguiente es una solución Java. Estoy seguro de que lo mismo podría hacerse en C # también. Evita tener que especificar el tipo en cualquier parte del código; en su lugar, debe especificarlo en las cadenas que está intentando analizar.
El problema es que no hay forma de saber con qué enumeración puede coincidir la Cadena, por lo que la respuesta es resolver ese problema.
En lugar de aceptar solo el valor de la cadena, acepte una cadena que tenga tanto la enumeración como el valor en la forma "enumeration.value". El código de trabajo está debajo: requiere Java 1.8 o posterior. Esto también haría que el XML sea más preciso, ya que vería algo como color = "Color.red" en lugar de simplemente color = "rojo".
Llamaría al método acceptEnumeratedValue () con una cadena que contiene el nombre del valor del punto nombre enum.
El método devuelve el valor enumerado formal.
fuente