Pergunta

Estou construindo uma função para estender o Enum.Parse conceito que

  • Permite que um valor padrão seja analisado caso um valor Enum não seja encontrado
  • Não faz distinção entre maiúsculas e minúsculas

Então escrevi o seguinte:

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;
}

Estou recebendo uma restrição de erro que não pode ser de classe especial System.Enum.

É justo, mas existe uma solução alternativa para permitir um Enum genérico ou terei que imitar o Parse função e passe um tipo como um atributo, o que força o feio requisito de boxe ao seu código.

EDITAR Todas as sugestões abaixo foram muito apreciadas, obrigado.

Decidi (deixei o loop para manter a insensibilidade a maiúsculas e minúsculas - estou usando isso ao analisar 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 fevereiro de 2015) Julien Lebosquain postou recentemente uma solução genérica com segurança de tipo imposta pelo compilador em MSIL ou F# abaixo, que vale a pena dar uma olhada e um voto positivo.Removerei esta edição se a solução borbulhar mais acima na página.

Foi útil?

Solução

Desde Enum Tipo de implementos IConvertible interface, uma implementação melhor deveria ser algo assim:

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

   //...
}

Isso ainda permitirá a passagem de tipos de valor implementando IConvertible.As chances são raras.

Outras dicas

Este recurso finalmente é compatível com C# 7.3!

O seguinte trecho (de as amostras dotnet) demonstra como:

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;
}

Certifique-se de definir a versão do idioma em seu projeto C# para a versão 7.3.


Resposta original abaixo:

Estou atrasado para o jogo, mas encarei isso como um desafio para ver como isso poderia ser feito.Não é possível em C# (ou VB.NET, mas role para baixo para F#), mas é possível em MSIL.Eu escrevi essa pequena...coisa

// 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
  }
} 

O que gera uma função que seria ficaria assim, se fosse C# válido:

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

Então com o seguinte 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
}

Infelizmente, isso significa ter esta parte do seu código escrita em MSIL em vez de C#, com o único benefício adicional sendo que você pode restringir esse método System.Enum.Também é meio chato, porque é compilado em um assembly separado.No entanto, isso não significa que você precise implantá-lo dessa maneira.

Ao remover a linha .assembly MyThing{} e invocando ilasm da seguinte forma:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

você obtém um netmodule em vez de um assembly.

Infelizmente, o VS2010 (e anteriores, obviamente) não suporta a adição de referências de netmodule, o que significa que você teria que deixá-lo em 2 assemblies separados durante a depuração.A única maneira de adicioná-los como parte de sua montagem seria executar o csc.exe usando o comando /addmodule:{files} argumento de linha de comando.Não seria também doloroso em um script MSBuild.Claro, se você for corajoso ou estúpido, você pode executar o csc manualmente todas as vezes.E certamente fica mais complicado à medida que vários assemblies precisam de acesso a ele.

Então, isso PODE ser feito em .Net.Vale a pena o esforço extra?Hum, bem, acho que vou deixar você decidir sobre isso.


Solução F# como alternativa

Crédito extra:Acontece que uma restrição genérica enum é possível em pelo menos uma outra linguagem .NET além do 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

Esta é mais fácil de manter, pois é uma linguagem bem conhecida com suporte completo ao Visual Studio IDE, mas você ainda precisa de um projeto separado em sua solução para ela.No entanto, produz naturalmente IL consideravelmente diferente (o código é muito diferente) e depende do FSharp.Core biblioteca, que, assim como qualquer outra biblioteca externa, precisa fazer parte da sua distribuição.

Veja como você pode usá-lo (basicamente igual à solução MSIL) e mostrar que ele falha corretamente em estruturas que de outra forma seriam sinônimas:

// 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 do C# 7.3 (disponível com Visual Studio 2017 ≥ v15.7), este código agora é completamente válido:

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

C# ≤ 7,2

Você pode ter uma restrição de enumeração imposta pelo compilador real, abusando da herança de restrição.O código a seguir especifica um class e um struct restrições ao mesmo tempo:

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");

Observação:isso é declarado especificamente na especificação da linguagem C# 5.0:

Se o parâmetro de tipo S depende do parâmetro de tipo T então:...] É válido para s ter a restrição de tipo de valor e t ter a restrição de tipo de referência.Efetivamente, isso limita t aos tipos System.Object, System.valuetype, System.enum e qualquer tipo de interface.

Editar

A questão foi agora soberbamente respondida por Julien Lebosquain.Gostaria também de estender a sua resposta com ignoreCase, defaultValue e argumentos opcionais, ao adicionar TryParse e 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
}

Exemplos 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);

Velho

Minhas antigas melhorias em A resposta de Vivek usando os comentários e 'novos' desenvolvimentos:

  • usar TEnum para maior clareza para os usuários
  • adicione mais restrições de interface para verificação de restrições adicional
  • deixar TryParse lidar ignoreCase com o parâmetro existente (introduzido no vs2010/.net 4)
  • opcionalmente, use o genérico default valor (introduzido em VS2005/.Net 2)
  • usar argumentos opcionais(introduzido no VS2010/.Net 4) com valores padrão, para defaultValue e ignoreCase

resultando em:

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;
    }
}

Você pode definir um construtor estático para a classe que verificará se o tipo T é um enum e lançará uma exceção se não for.Este é o método mencionado por Jeffery Richter em seu livro CLR via C#.

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

Então, no método de análise, você pode simplesmente usar Enum.Parse(typeof(T), input, true) para converter de string em enum.O último parâmetro verdadeiro é para ignorar maiúsculas e minúsculas da entrada.

Também deve ser considerado que, desde o lançamento do C# 7.3, o uso de restrições Enum é suportado imediatamente, sem a necessidade de verificações adicionais e outras coisas.

Portanto, daqui para frente e como você alterou a versão da linguagem do seu projeto para C# 7.3, o código a seguir funcionará perfeitamente:

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

Caso você não saiba como alterar a versão da linguagem para C# 7.3 veja a seguinte captura de tela:enter image description here

EDIT 1 - Versão necessária do Visual Studio e considerando o ReSharper

Para que o Visual Studio reconheça a nova sintaxe, você precisa de pelo menos a versão 15.7.Você pode encontrar isso também mencionado nas notas de lançamento da Microsoft, consulte Notas de versão do Visual Studio 2017 15.7.Obrigado @MohamedElshawaf por apontar esta questão válida.

Por favor, observe também que, no meu caso, o ReSharper 2018.1, no momento em que escrevi este EDIT, ainda não suporta C# 7.3.Tendo o ReSharper ativado, ele destaca a restrição Enum como um erro, informando-me Não é possível usar 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'object' como restrição de parâmetro de tipo.ReSharper sugere como uma solução rápida para Remova a restrição 'Enum' do parâmetro de tipo T do método

No entanto, se você desativar o ReSharper temporariamente em Ferramentas -> Opções -> ReSharper Ultimate -> Geral você verá que a sintaxe está perfeitamente correta, visto que você usa VS 15.7 ou superior e C# 7.3 ou superior.

Modifiquei a amostra do dimarzionista.Esta versão funcionará apenas com Enums e não permitirá a passagem de estruturas.

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);
    }
}

Tentei melhorar um pouco o 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;
}

Eu tenho um requisito específico onde preciso usar enum com texto associado ao valor enum.Por exemplo, quando uso enum para especificar o tipo de erro, é necessário descrever os detalhes do erro.

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 isso seja ú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 isso é possível em outros idiomas (C++ gerenciado, IL diretamente).

Citar:

...Na verdade, ambas as restrições produzem IL válida e também podem ser consumidas por C# se escritas em outra linguagem (você pode declarar essas restrições em C++ gerenciado ou em IL).

Quem sabe

Esta é a minha opinião.Combinado das respostas e do 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));
    }
}

Fonte MSDN

As respostas existentes são verdadeiras em C# <=7.2.No entanto, existe uma linguagem C# solicitação de recurso (vinculado a um corefx solicitação de recurso) para permitir o seguinte;

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

No momento em que este artigo foi escrito, o recurso estava “Em discussão” nas Reuniões de Desenvolvimento Linguístico.

EDITAR

Conforme nawfalinformações, isso está sendo introduzido em C# 7.3.

Sempre gostei disso (você pode modificar conforme apropriado):

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>();
}

Adorei a solução de Christopher Currens usando IL, mas para aqueles que não querem lidar com tarefas complicadas de incluir MSIL em seu processo de construção, escrevi uma função semelhante em C#.

Observe que você não pode usar restrições genéricas como where T : Enum porque Enum é um tipo especial.Portanto, tenho que verificar se determinado tipo genérico é realmente enum.

Minha função é:

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;
}

Encapsulei a solução de Vivek em uma classe de utilitário que você pode reutilizar.Observe que você ainda deve definir restrições de tipo "onde T :struct, IConvertible" no seu 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);
        }
    }
}

Eu criei um método de extensão to get integer value from enum dê uma olhada na implementação do 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);
}

isso é uso

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

Conforme afirmado em outras respostas anteriores;embora isso não possa ser expresso no código-fonte, na verdade pode ser feito no nível IL.@Christopher Currens responder mostra como o IL faz isso.

Com FodySuplemento ExtraConstraints.Fody há uma maneira muito simples, completa com ferramentas de construção, de conseguir isso.Basta adicionar seus pacotes de pepitas (Fody, ExtraConstraints.Fody) ao seu projeto e adicione as restrições da seguinte forma (Trecho do Leiame de ExtraConstraints):

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

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

e Fody adicionará o IL necessário para que a restrição esteja presente.Observe também o recurso adicional de restringir delegados:

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

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

Em relação aos Enums, você também pode querer observar o altamente interessante Enums.NET.

Se não houver problema em usar a transmissão direta depois, acho que você pode usar o System.Enum classe base em seu método, sempre que necessário.Você só precisa substituir os parâmetros de tipo com cuidado.Então a implementação do método seria assim:

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;
    }
}

Então você pode usá-lo como:

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

Apenas para completar, a seguir está uma solução Java.Tenho certeza de que o mesmo também poderia ser feito em C#.Isso evita a necessidade de especificar o tipo em qualquer lugar do código - em vez disso, você o especifica nas strings que está tentando analisar.

O problema é que não há como saber a qual enumeração a String pode corresponder - então a resposta é resolver esse problema.

Em vez de aceitar apenas o valor da string, aceite uma String que tenha a enumeração e o valor no formato "enumeration.value".O código funcional está abaixo - requer Java 1.8 ou posterior.Isso também tornaria o XML mais preciso, pois você veria algo como color="Color.red" em vez de apenas color="red".

Você chamaria o método acceptEnumeratedValue() com uma string contendo o nome da enumeração, ponto, valor, nome.

O método retorna o 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top