Pregunta

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
  • No distingue entre 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 un error. La restricción no puede ser una clase especial. System.Enum.

Es justo, pero ¿existe alguna solución para permitir una enumeración genérica o tendré que imitar la Parse funciona y pasa un tipo como atributo, lo que impone el feo requisito de boxeo en su código.

EDITAR Todas las sugerencias a continuación han sido muy apreciadas, gracias.

Me he decidido (dejé el bucle para no distinguir entre 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 con seguridad de tipos impuesta por el compilador en MSIL o F# a continuación, que bien merece una mirada y un voto positivo.Eliminaré esta edición si la solución aparece más arriba en la página.

¿Fue útil?

Solución

Desde Enum Tipo implementos IConvertible interfaz, una mejor implementación debería ser algo como esto:

public T GetEnumFromString<T>(string value) where T : struct, IConvertible
{
   if (!typeof(T).IsEnum) 
   {
      throw new ArgumentException("T must be an enumerated type");
   }

   //...
}

Esto aún permitirá pasar tipos de valores que implementen IConvertible.Sin embargo, las posibilidades son raras.

Otros consejos

¡Esta característica finalmente es compatible con C# 7.3!

El siguiente fragmento (de las muestras dotnet) demuestra cómo:

public static Dictionary<int, string> EnumNamedValues<T>() where T : System.Enum
{
    var result = new Dictionary<int, string>();
    var values = Enum.GetValues(typeof(T));

    foreach (int item in values)
        result.Add(item, Enum.GetName(typeof(T), item));
    return result;
}

Asegúrese de configurar la versión de su idioma en su proyecto C# en la versión 7.3.


Respuesta original a continuación:

Llegué tarde al juego, pero lo tomé como un desafío para ver cómo se podía hacer.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.

// license: http://www.apache.org/licenses/LICENSE-2.0.html
.assembly MyThing{}
.class public abstract sealed MyThing.Thing
       extends [mscorlib]System.Object
{
  .method public static !!T  GetEnumFromString<valuetype .ctor ([mscorlib]System.Enum) T>(string strValue,
                                                                                          !!T defaultValue) cil managed
  {
    .maxstack  2
    .locals init ([0] !!T temp,
                  [1] !!T return_value,
                  [2] class [mscorlib]System.Collections.IEnumerator enumerator,
                  [3] class [mscorlib]System.IDisposable disposer)
    // if(string.IsNullOrEmpty(strValue)) return defaultValue;
    ldarg strValue
    call bool [mscorlib]System.String::IsNullOrEmpty(string)
    brfalse.s HASVALUE
    br RETURNDEF         // return default it empty

    // foreach (T item in Enum.GetValues(typeof(T)))
  HASVALUE:
    // Enum.GetValues.GetEnumerator()
    ldtoken !!T
    call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    call class [mscorlib]System.Array [mscorlib]System.Enum::GetValues(class [mscorlib]System.Type)
    callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Array::GetEnumerator() 
    stloc enumerator
    .try
    {
      CONDITION:
        ldloc enumerator
        callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
        brfalse.s LEAVE

      STATEMENTS:
        // T item = (T)Enumerator.Current
        ldloc enumerator
        callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
        unbox.any !!T
        stloc temp
        ldloca.s temp
        constrained. !!T

        // if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        callvirt instance string [mscorlib]System.Object::ToString()
        callvirt instance string [mscorlib]System.String::ToLower()
        ldarg strValue
        callvirt instance string [mscorlib]System.String::Trim()
        callvirt instance string [mscorlib]System.String::ToLower()
        callvirt instance bool [mscorlib]System.String::Equals(string)
        brfalse.s CONDITION
        ldloc temp
        stloc return_value
        leave.s RETURNVAL

      LEAVE:
        leave.s RETURNDEF
    }
    finally
    {
        // ArrayList's Enumerator may or may not inherit from IDisposable
        ldloc enumerator
        isinst [mscorlib]System.IDisposable
        stloc.s disposer
        ldloc.s disposer
        ldnull
        ceq
        brtrue.s LEAVEFINALLY
        ldloc.s disposer
        callvirt instance void [mscorlib]System.IDisposable::Dispose()
      LEAVEFINALLY:
        endfinally
    }

  RETURNDEF:
    ldarg defaultValue
    stloc return_value

  RETURNVAL:
    ldloc return_value
    ret
  }
} 

Lo cual genera una función que haría se vería así, si fuera C# válido:

T GetEnumFromString<T>(string valueString, T defaultValue) where T : Enum

Luego con el siguiente código C#:

using MyThing;
// stuff...
private enum MyEnum { Yes, No, Okay }
static void Main(string[] args)
{
    Thing.GetEnumFromString("No", MyEnum.Yes); // returns MyEnum.No
    Thing.GetEnumFromString("Invalid", MyEnum.Okay);  // returns MyEnum.Okay
    Thing.GetEnumFromString("AnotherInvalid", 0); // compiler error, not an Enum
}

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 mediante System.Enum.También es un poco fastidioso, porque se compila en un ensamblado separado.Sin embargo, eso no significa que tengas que implementarlo de esa manera.

Al eliminar la línea .assembly MyThing{} e invocando el ilasm de la siguiente manera:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

obtienes un netmodule en lugar de un ensamblaje.

Desafortunadamente, VS2010 (y versiones anteriores, obviamente) no admiten la adición de referencias de netmodule, lo que significa que tendría que dejarlo en 2 ensamblados separados cuando esté depurando.La única forma de agregarlos como parte de su ensamblaje sería ejecutar csc.exe usted mismo usando el /addmodule:{files} argumento de la línea de comando.no seria también 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 a medida que múltiples ensamblajes necesitan acceder a él.

Entonces, SE PUEDE hacer 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 una restricción genérica sobre enum es posible en al menos otro lenguaje .NET además de MSIL:F#.

type MyThing =
    static member GetEnumFromString<'T when 'T :> Enum> str defaultValue: 'T =
        /// protect for null (only required in interop with C#)
        let str = if isNull str then String.Empty else str

        Enum.GetValues(typedefof<'T>)
        |> Seq.cast<_>
        |> Seq.tryFind(fun v -> String.Compare(v.ToString(), str.Trim(), true) = 0)
        |> function Some x -> x | None -> defaultValue

Este es más fácil de mantener ya que es un lenguaje bien conocido con soporte completo para Visual Studio IDE, pero aún necesita un proyecto separado en su solución.Sin embargo, naturalmente produce 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 estructuras que de otro modo serían sinónimos:

// works, result is inferred to have type StringComparison
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", StringComparison.Ordinal);
// type restriction is recognized by C#, this fails at compile time
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", 42);

C# ≥ 7,3

A partir de C# 7.3 (disponible con Visual Studio 2017 ≥ v15.7), este código ahora es completamente válido:

public static TEnum Parse<TEnum>(string value)
where TEnum : struct, Enum { ... }

C# ≤ 7,2

Puede hacer que un compilador real aplique una restricción de enumeración abusando de la herencia de restricciones.El siguiente código especifica tanto un class y un struct restricciones al mismo tiempo:

public abstract class EnumClassUtils<TClass>
where TClass : class
{

    public static TEnum Parse<TEnum>(string value)
    where TEnum : struct, TClass
    {
        return (TEnum) Enum.Parse(typeof(TEnum), value);
    }

}

public class EnumUtils : EnumClassUtils<Enum>
{
}

Uso:

EnumUtils.Parse<SomeEnum>("value");

Nota:esto se indica específicamente en la especificación del lenguaje C# 5.0:

Si el parámetro de tipo S depende del parámetro de tipo T entonces:...] Es válido que S tenga la restricción de tipo de valor y t para tener la restricción de tipo de referencia.Efectivamente, esto limita t a los tipos System.Object, System.ValueType, System.enum y cualquier tipo de interfaz.

Editar

La pregunta ahora ha sido magníficamente respondida por Julien Lebosquain.También me gustaría ampliar su respuesta con ignoreCase, defaultValue y argumentos opcionales, mientras agrega TryParse y ParseOrDefault.

public abstract class ConstrainedEnumParser<TClass> where TClass : class
// value type constraint S ("TEnum") depends on reference type T ("TClass") [and on struct]
{
    // internal constructor, to prevent this class from being inherited outside this code
    internal ConstrainedEnumParser() {}
    // Parse using pragmatic/adhoc hard cast:
    //  - struct + class = enum
    //  - 'guaranteed' call from derived <System.Enum>-constrained type EnumUtils
    public static TEnum Parse<TEnum>(string value, bool ignoreCase = false) where TEnum : struct, TClass
    {
        return (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase);
    }
    public static bool TryParse<TEnum>(string value, out TEnum result, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        var didParse = Enum.TryParse(value, ignoreCase, out result);
        if (didParse == false)
        {
            result = defaultValue;
        }
        return didParse;
    }
    public static TEnum ParseOrDefault<TEnum>(string value, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum result;
        if (Enum.TryParse(value, ignoreCase, out result)) { return result; }
        return defaultValue;
    }
}

public class EnumUtils: ConstrainedEnumParser<System.Enum>
// reference type constraint to any <System.Enum>
{
    // call to parse will then contain constraint to specific <System.Enum>-class
}

Ejemplos de uso:

WeekDay parsedDayOrArgumentException = EnumUtils.Parse<WeekDay>("monday", ignoreCase:true);
WeekDay parsedDayOrDefault;
bool didParse = EnumUtils.TryParse<WeekDay>("clubs", out parsedDayOrDefault, ignoreCase:true);
parsedDayOrDefault = EnumUtils.ParseOrDefault<WeekDay>("friday", ignoreCase:true, defaultValue:WeekDay.Sunday);

Viejo

Mis viejas mejoras en La respuesta de Vivek. utilizando los comentarios y los 'nuevos' desarrollos:

  • usar TEnum para mayor claridad para los usuarios
  • agregue más restricciones de interfaz para una verificación de restricciones adicional
  • dejar TryParse manejar ignoreCase con el parámetro existente (introducido en VS2010/.NET 4)
  • Opcionalmente use el genérico. default valor (introducido en VS2005/.Net 2)
  • usar argumentos opcionales(introducido en VS2010/.Net 4) con valores predeterminados, para defaultValue y ignoreCase

Resultando en:

public static class EnumUtils
{
    public static TEnum ParseEnum<TEnum>(this string value,
                                         bool ignoreCase = true,
                                         TEnum defaultValue = default(TEnum))
        where TEnum : struct,  IComparable, IFormattable, IConvertible
    {
        if ( ! typeof(TEnum).IsEnum) { throw new ArgumentException("TEnum must be an enumerated type"); }
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum lResult;
        if (Enum.TryParse(value, ignoreCase, out lResult)) { return lResult; }
        return defaultValue;
    }
}

Puede definir un constructor estático para la clase que verificará que el tipo T sea una enumeración y generará una excepción si no lo es.Este es el método mencionado por Jeffery Richter en su libro CLR vía C#.

internal sealed class GenericTypeThatRequiresAnEnum<T> {
    static GenericTypeThatRequiresAnEnum() {
        if (!typeof(T).IsEnum) {
        throw new ArgumentException("T must be an enumerated type");
        }
    }
}

Luego, en el método de análisis, puede usar Enum.Parse(typeof(T), input, true) para convertir de cadena a enumeración.El último parámetro verdadero es para ignorar el caso de la entrada.

También se debe considerar que desde el lanzamiento de C# 7.3, el uso de restricciones Enum se admite de manera inmediata sin tener que realizar comprobaciones adicionales y demás.

Entonces, en el futuro, y dado que ha cambiado la versión de idioma de su proyecto a C# 7.3, el siguiente código funcionará perfectamente bien:

    private static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
    {
        // Your code goes here...
    }

En caso de que no sepa cómo cambiar la versión de idioma a C# 7.3, consulte la siguiente captura de pantalla:enter image description here

EDITAR 1: versión requerida de Visual Studio y consideración de 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 Notas de la versión de Visual Studio 2017 15.7.Gracias @MohamedElshawaf por señalar esta pregunta válida.

Tenga en cuenta también que, en mi caso, ReSharper 2018.1 al momento de escribir esta EDICIÓN aún no es compatible con C# 7.3.Al activar ReSharper, se resalta la restricción Enum como un error que me indica No se puede utilizar 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'object' como restricción de parámetro de tipo.ReSharper sugiere como solución rápida 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ás que la sintaxis está perfectamente bien dado que usas VS 15.7 o superior y C# 7.3 o superior.

Modifiqué la muestra por dimarsionista.Esta versión solo funcionará con Enums y no permitirá que las estructuras pasen.

public static T ParseEnum<T>(string enumString)
    where T : struct // enum 
    {
    if (String.IsNullOrEmpty(enumString) || !typeof(T).IsEnum)
       throw new Exception("Type given must be an Enum");
    try
    {

       return (T)Enum.Parse(typeof(T), enumString, true);
    }
    catch (Exception ex)
    {
       return default(T);
    }
}

Intenté mejorar un poco el código:

public T LoadEnum<T>(string value, T defaultValue = default(T)) where T : struct, IComparable, IFormattable, IConvertible
{
    if (Enum.IsDefined(typeof(T), value))
    {
        return (T)Enum.Parse(typeof(T), value, true);
    }
    return defaultValue;
}

Tengo un requisito específico en el que necesito usar una enumeración con texto asociado al valor de enumeración.Por ejemplo, cuando uso enum para especificar el tipo de error, es necesario describir los detalles del error.

public static class XmlEnumExtension
{
    public static string ReadXmlEnumAttribute(this Enum value)
    {
        if (value == null) throw new ArgumentNullException("value");
        var attribs = (XmlEnumAttribute[]) value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof (XmlEnumAttribute), true);
        return attribs.Length > 0 ? attribs[0].Name : value.ToString();
    }

    public static T ParseXmlEnumAttribute<T>(this string str)
    {
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            var attribs = (XmlEnumAttribute[])item.GetType().GetField(item.ToString()).GetCustomAttributes(typeof(XmlEnumAttribute), true);
            if(attribs.Length > 0 && attribs[0].Name.Equals(str)) return item;
        }
        return (T)Enum.Parse(typeof(T), str, true);
    }
}

public enum MyEnum
{
    [XmlEnum("First Value")]
    One,
    [XmlEnum("Second Value")]
    Two,
    Three
}

 static void Main()
 {
    // Parsing from XmlEnum attribute
    var str = "Second Value";
    var me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    // Parsing without XmlEnum
    str = "Three";
    me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    me = MyEnum.One;
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
}

Espero que esto sea útil:

public static TValue ParseEnum<TValue>(string value, TValue defaultValue)
                  where TValue : struct // enum 
{
      try
      {
            if (String.IsNullOrEmpty(value))
                  return defaultValue;
            return (TValue)Enum.Parse(typeof (TValue), value);
      }
      catch(Exception ex)
      {
            return defaultValue;
      }
}

Curiosamente, aparentemente esto es posible en otros idiomas (Administrado C++, IL directamente).

Citar:

...En realidad, ambas restricciones producen IL válida y C# también puede consumirlas si están escritas en otro lenguaje (puede declarar esas restricciones en C++ administrado o en IL).

Quién sabe

Esta es mi opinión.Combinado de las respuestas y MSDN

public static TEnum ParseToEnum<TEnum>(this string text) where TEnum : struct, IConvertible, IComparable, IFormattable
{
    if (string.IsNullOrEmpty(text) || !typeof(TEnum).IsEnum)
        throw new ArgumentException("TEnum must be an Enum type");

    try
    {
        var enumValue = (TEnum)Enum.Parse(typeof(TEnum), text.Trim(), true);
        return enumValue;
    }
    catch (Exception)
    {
        throw new ArgumentException(string.Format("{0} is not a member of the {1} enumeration.", text, typeof(TEnum).Name));
    }
}

Fuente MSDN

Las respuestas existentes son verdaderas a partir de C# <=7.2.Sin embargo, existe un lenguaje C#. solicitud de función (atado a un corefx solicitud de función) para permitir lo siguiente;

public class MyGeneric<TEnum> where TEnum : System.Enum
{ }

Al momento de escribir este artículo, la característica está "en discusión" en las Reuniones de Desarrollo del Lenguaje.

EDITAR

según nafalInformación, esto se está introduciendo en C#. 7.3.

Siempre me gustó esto (podrías modificarlo según corresponda):

public static IEnumerable<TEnum> GetEnumValues()
{
  Type enumType = typeof(TEnum);

  if(!enumType.IsEnum)
    throw new ArgumentException("Type argument must be Enum type");

  Array enumValues = Enum.GetValues(enumType);
  return enumValues.Cast<TEnum>();
}

Me encantó la solución de Christopher Currens que utiliza IL, pero para aquellos que no quieren lidiar con el complicado asunto de incluir MSIL en su proceso de compilación, escribí una función similar en C#.

Sin embargo, tenga en cuenta que no puede utilizar restricciones genéricas como where T : Enum porque Enum es de tipo especial.Por lo tanto, tengo que comprobar si el tipo genérico dado es realmente enumeración.

Mi función es:

public static T GetEnumFromString<T>(string strValue, T defaultValue)
{
    // Check if it realy enum at runtime 
    if (!typeof(T).IsEnum)
        throw new ArgumentException("Method GetEnumFromString can be used with enums only");

    if (!string.IsNullOrEmpty(strValue))
    {
        IEnumerator enumerator = Enum.GetValues(typeof(T)).GetEnumerator();
        while (enumerator.MoveNext())
        {
            T temp = (T)enumerator.Current;
            if (temp.ToString().ToLower().Equals(strValue.Trim().ToLower()))
                return temp;
        }
    }

    return defaultValue;
}

He encapsulado la solución de Vivek en una clase de utilidad que puedes reutilizar.Tenga en cuenta que aún debe definir restricciones de tipo "donde T:struct, IConvertible" según su tipo.

using System;

internal static class EnumEnforcer
{
    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your type parameter {0} is an enum.",
                typeParameterName,
                methodName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    /// <param name="inputParameterName">Name of the input parameter of this page.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName, string inputParameterName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your input parameter {2} is of correct type.",
                typeParameterName,
                methodName,
                inputParameterName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="exceptionMessage">Message to show in case T is not an enum.</param>
    public static void EnforceIsEnum<T>(string exceptionMessage)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException(exceptionMessage);
        }
    }
}

Creé un método de extensión to get integer value from enum echar un vistazo a la implementación del método

public static int ToInt<T>(this T soure) where T : IConvertible//enum
{
    if (typeof(T).IsEnum)
    {
        return (int) (IConvertible)soure;// the tricky part
    }
    //else
    //    throw new ArgumentException("T must be an enumerated type");
    return soure.ToInt32(CultureInfo.CurrentCulture);
}

este es el uso

MemberStatusEnum.Activated.ToInt()// using extension Method
(int) MemberStatusEnum.Activated //the ordinary way

Como se indicó en otras respuestas anteriores;Si bien esto no se puede expresar en el código fuente, en realidad se puede hacer en el nivel IL.@Christopher Currens respuesta muestra cómo el IL hace eso.

Con fodycomplemento Restricciones adicionales.Fody Hay una forma muy sencilla, completa con herramientas de compilación, de lograrlo.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):

public void MethodWithEnumConstraint<[EnumConstraint] T>() {...}

public void MethodWithTypeEnumConstraint<[EnumConstraint(typeof(ConsoleColor))] T>() {...}

y Fody agregará el IL necesario para que la restricción esté presente.Tenga en cuenta también la característica adicional de restringir delegados:

public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{...}

public void MethodWithTypeDelegateConstraint<[DelegateConstraint(typeof(Func<int>))] T> ()
{...}

En cuanto a las enumeraciones, es posible que también desees tomar nota de las muy interesantes Enumeraciones.NET.

Si está bien usar el casting directo después, supongo que puedes usar el System.Enum clase base en su método, cuando sea necesario.Sólo necesita reemplazar los parámetros de tipo con cuidado.Entonces la implementación del método sería como:

public static class EnumUtils
{
    public static Enum GetEnumFromString(string value, Enum defaultValue)
    {
        if (string.IsNullOrEmpty(value)) return defaultValue;
        foreach (Enum item in Enum.GetValues(defaultValue.GetType()))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}

Entonces puedes usarlo como:

var parsedOutput = (YourEnum)EnumUtils.GetEnumFromString(someString, YourEnum.DefaultValue);

Para completar, la siguiente es una solución Java.Estoy seguro de que también se podría hacer lo mismo en C#.Evita tener que especificar el tipo en cualquier parte del código; en su lugar, lo especifica en las cadenas que está intentando analizar.

El problema es que no hay forma de saber con qué enumeración podría 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 el formato "enumeración.valor".El código de trabajo se encuentra a continuación: 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 solo color="red".

Llamaría al método AcceptEnumeatedValue() con una cadena que contenga el nombre del valor de punto del nombre de enumeración.

El método devuelve el valor enumerado formal.

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;


public class EnumFromString {

    enum NumberEnum {One, Two, Three};
    enum LetterEnum {A, B, C};


    Map<String, Function<String, ? extends Enum>> enumsByName = new HashMap<>();

    public static void main(String[] args) {
        EnumFromString efs = new EnumFromString();

        System.out.print("\nFirst string is NumberEnum.Two - enum is " + efs.acceptEnumeratedValue("NumberEnum.Two").name());
        System.out.print("\nSecond string is LetterEnum.B - enum is " + efs.acceptEnumeratedValue("LetterEnum.B").name());

    }

    public EnumFromString() {
        enumsByName.put("NumberEnum", s -> {return NumberEnum.valueOf(s);});
        enumsByName.put("LetterEnum", s -> {return LetterEnum.valueOf(s);});
    }

    public Enum acceptEnumeratedValue(String enumDotValue) {

        int pos = enumDotValue.indexOf(".");

        String enumName = enumDotValue.substring(0, pos);
        String value = enumDotValue.substring(pos + 1);

        Enum enumeratedValue = enumsByName.get(enumName).apply(value);

        return enumeratedValue;
    }


}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top