it-swarm-pt.tech

Criar método genérico restringindo T a um Enum

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

  • Permite que um valor padrão seja analisado no caso de um valor Enum não ser encontrado
  • É insensível a maiúsculas e minúsculas

Então eu 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 não pode ser classe especial System.Enum.

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

EDITTodas as sugestões abaixo foram muito apreciadas, obrigado.

Tenha resolvido (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;
    }
}

EDIT: (16 de fevereiro de 2015) Julien Lebosquain publicou recentemente um compilador reforçou a solução genérica segura de tipos em MSIL ou F # abaixo, que vale bem a pena dar uma olhada, e um upvote. Eu removerei esta edição se a solução borbulhar na página.

1052
johnc

Como Enum Type implementa a interface IConvertible, uma implementação melhor deve 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 embora.

931
Vivek

Este recurso é finalmente suportado no C # 7.3!

O trecho a seguir (de os exemplos de dotnet ) demonstra seu uso:

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 sua versão de idioma no seu projeto C # para a versão 7.3.


Resposta Original abaixo:

Estou atrasado para o jogo, mas aceitei como um desafio para ver como isso poderia ser feito. Não é possível em C # (ou VB.NET, mas desloque-se para F #), mas é possível em MSIL. Eu escrevi essa coisinha ....

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

Que gera uma função que would se parece com isso, se fosse válido C #:

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 essa parte do seu código escrita em MSIL em vez de C #, com o único benefício adicional sendo que você é capaz de restringir esse método por System.Enum. Também é meio chato, porque é compilado em um Assembly separado. No entanto, isso não significa que você tenha que implantá-lo dessa maneira.

Removendo a linha .Assembly MyThing{} e invocando o ilasm como segue:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

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

Infelizmente, o VS2010 (e anteriormente, obviamente) não suporta a adição de referências de netmodule, o que significa que você teria que deixá-lo em 2 montagens separadas quando estiver depurando. A única maneira de adicioná-los como parte de seu Assembly seria executar o csc.exe usando o argumento de linha de comando /addmodule:{files}. Não seria também doloroso em um script do MSBuild. Claro, se você for corajoso ou estúpido, você pode executar o csc manualmente a cada vez. E certamente fica mais complicado, pois vários conjuntos precisam acessá-lo.

Então, isso pode ser feito no .net. Vale a pena o esforço extra? Bem, acho que vou deixar você decidir sobre isso.


Solução F # como alternativa

Crédito Extra: Acontece que uma restrição genérica em enum é possível em pelo menos uma outra linguagem .NET além 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 é mais fácil de manter, já que é uma linguagem bem conhecida com suporte completo ao Visual Studio IDE, mas você ainda precisa de um projeto separado em sua solução para isso. No entanto, ele naturalmente produz IL consideravelmente diferente (o código é muito diferente) e depende da biblioteca FSharp.Core, que, como qualquer outra biblioteca externa, precisa fazer parte de sua distribuição.

Veja como você pode usá-lo (basicamente o mesmo que a solução MSIL) e para mostrar que ele falha corretamente em estruturas de 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);
525
Christopher Currens

C # ≥ 7,3

Começando com o C # 7.3 (disponível com o Visual Studio 2017 ≥ v15.7), esse 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 enum imposta pelo compilador real ao abusar da herança de restrição. O código a seguir especifica as restrições class e struct 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");

Nota: isso é especificamente indicado na especificação da linguagem C # 5.0:

Se o parâmetro de tipo S depender 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.

181
Julien Lebosquain

Editar

A pergunta agora foi soberbamente respondida por Julien Lebosquain . Eu também gostaria de estender sua resposta com ignoreCase, defaultValue e argumentos opcionais, enquanto adicionando 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);

antigo

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

  • use TEnum para maior clareza para os usuários
  • adicionar mais restrições de interface para verificação de restrição adicional
  • deixe TryParse handle ignoreCase com o parâmetro existente (introduzido no VS2010/.Net 4)
  • opcionalmente, use o valor genérico default (introduzido no VS2005/.Net 2)
  • use argumentos opcionais (introduzidos 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;
    }
}
30
Yahoo Serious

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

Em seguida, no método de análise, você pode usar apenas Enum.Parse (typeof (T), input, true) para converter de string para o enum. O último parâmetro true é para ignorar o caso da entrada.

18
Karg

Também deve ser considerado que desde o lançamento do C # 7.3 usando restrições Enum é suportado out-of-the-box sem ter que fazer verificação adicional e outras coisas.

Então, indo em frente e dado que você mudou a versão do seu projeto para o 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 do idioma para o C # 7.3, consulte a seguinte captura de tela: enter image description here

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

Para o Visual Studio reconhecer 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.

Pls também nota que, no meu caso, o ReSharper 2018.1 a partir da gravação deste EDIT ainda não suporta o C # 7.3. Tendo ReSharper ativado ele destaca a restrição Enum como um erro me dizendo Não é possível usar 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'objeto' como restrição de parâmetro de tipo. ReSharper sugere como uma solução rápida para Remover a restrição 'Enum' do tipo de parâmetro T do método

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

13
baumgarb

Eu modifiquei a amostra por dimarzionist. Essa versão só funcionará com Enums e não permitirá que as structs passem.

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

Eu tentei melhorar o código um pouco:

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

Eu tenho requisito específico onde eu precisava usar enum com texto associado ao valor de enum. Por exemplo, quando eu 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());
}
5
Sunny Rajwadi

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

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

MSDN fonte

3
KarmaEDV

Curiosamente, aparentemente isso é possível em outros idiomas (Managed C++, IL diretamente).

Citar:

... Ambas as restrições realmente produzem um IL válido e também podem ser consumidas pelo C # se escritas em outro idioma (você pode declarar essas restrições no C++ gerenciado ou no IL).

Quem sabe

3
Andrew Backer

As respostas existentes são verdadeiras a partir de C # <= 7.2. No entanto, há uma linguagem C # solicitação de recurso (ligada a um pedido de recurso corefx ) para permitir o seguinte;

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

No momento da redação, o recurso é "Em discussão" nas Reuniões de Desenvolvimento de Idiomas.

EDIT

Conforme as informações do nawfal , isso está sendo introduzido em C # 7.3 .

3
DiskJunky

Eu encapsulei a solução da Vivek em uma classe de utilitário que você pode reutilizar. Por favor, note 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);
        }
    }
}
1
niaher

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

Como afirmado em outras respostas antes; enquanto isso não pode ser expresso no código-fonte, pode ser feito no nível IL. @Christopher Currens resposta mostra como o IL faz para isso.

Com Fody s Add-In ExtraConstraints.Fody há uma maneira muito simples, completa com ferramentas de construção, para conseguir isso. Basta adicionar seus pacotes nuget (Fody, ExtraConstraints.Fody) ao seu projeto e adicionar as restrições da seguinte forma (Trecho do Leia-me de ExtraConstraints):

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

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

e Fody adicionará o IL necessário para a restrição estar presente. Observe também o recurso adicional de delegação de representantes:

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

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

Em relação Enums, você também pode querer tomar nota do altamente interessante Enums.NET .

1
BatteryBackupUnit

Eu criei uma extensão Método to get integer value from enum veja a 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
1
Basheer AL-MOMANI

Eu amei a solução de Christopher Currens usando IL, mas para aqueles que não querem lidar com negócios complicados de incluir MSIL em seu processo de compilação eu escrevi uma função similar em C #.

Por favor, note que você não pode usar restrição genérica como where T : Enum porque Enum é um tipo especial. Portanto, eu 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;
}
1
expert

Se não houver problema em usar o casting direto depois, acho que você pode usar a classe base System.Enum 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 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;
    }
}

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

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