Enum ToString com strings amigáveis


282

Minha enum consiste nos seguintes valores:

private enum PublishStatusses{
    NotCompleted,
    Completed,
    Error
};

Eu quero ser capaz de gerar esses valores de uma maneira amigável ao usuário.
Não preciso ser capaz de passar da string para o valor novamente.


possível duplicata de enums C # corda
Nawfal

Possível duplicado da representação de um Enum
Liam

Respostas:


350

Eu uso o Descriptionatributo do espaço para nome System.ComponentModel. Simplesmente decore a enum:

private enum PublishStatusValue
{
    [Description("Not Completed")]
    NotCompleted,
    Completed,
    Error
};

Em seguida, use este código para recuperá-lo:

public static string GetDescription<T>(this T enumerationValue)
    where T : struct
{
    Type type = enumerationValue.GetType();
    if (!type.IsEnum)
    {
        throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
    }

    //Tries to find a DescriptionAttribute for a potential friendly name
    //for the enum
    MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
    if (memberInfo != null && memberInfo.Length > 0)
    {
        object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

        if (attrs != null && attrs.Length > 0)
        {
            //Pull out the description value
            return ((DescriptionAttribute)attrs[0]).Description;
        }
    }
    //If we have no description attribute, just return the ToString of the enum
    return enumerationValue.ToString();
}

12
Este exemplo é mais fácil de ler. stackoverflow.com/questions/1415140/…
RayLoveless

31
Suspeito que haja um impacto significativo no desempenho ao usar a reflexão, conforme descrito nesta solução. O código para o método de Will de usar o método de extensão ToFriendlyString é muito mais fácil de entender e seu desempenho também deve ser extremamente rápido.
Humbads

1
Eu gosto da versão que o @RayL vinculou, pois apenas adiciona o método de extensão ao Enums. Se isso é tudo que você quer usar isso para (conforme indicado com o ArgumentException, então não há nenhuma razão para que o método seja completamente genérico.
krillgar

4
Isso significa que cada enum precisa de seu próprio método de extensão. Este é um uso mais geral e requer mais trabalho, mas você provavelmente deseja quantificar o que "rápido" significa antes de decidirmos sobre o desempenho.
Raio Booysen

2
@petar que funciona, mas não se você deseja que strings amigáveis ​​sejam exibidas para os usuários. MY_TYPE terá o sublinhado e não é personalizável.
Ray Booysen 31/08/16

354

Eu faço isso com métodos de extensão:

public enum ErrorLevel
{
  None,
  Low,
  High,
  SoylentGreen
}

public static class ErrorLevelExtensions
{
  public static string ToFriendlyString(this ErrorLevel me)
  {
    switch(me)
    {
      case ErrorLevel.None:
        return "Everything is OK";
      case ErrorLevel.Low:
        return "SNAFU, if you know what I mean.";
      case ErrorLevel.High:
        return "Reaching TARFU levels";
      case ErrorLevel.SoylentGreen:
        return "ITS PEOPLE!!!!";
      default:
        return "Get your damn dirty hands off me you FILTHY APE!";
    }
  }
}

6
Isso é muito mais limpo que a resposta do Atributo. Agradável!
Pennyrave

3
@pennyrave: Eh. Muitos componentes da interface do usuário esperam encontrar e usar DisplayNameAttribute e DescriptionAttribute. De fato, agora, eu uso esses métodos e um método de extensão para obter facilmente esses valores.

60
O problema que vejo com isso é que você está constantemente escrevendo esses métodos de extensão. Com o mecanismo de atributo, é uma maneira simples de decorá-lo e apenas chamar um método.
Ray Booysen

5
Não tenho certeza do que você quer dizer?
Ray Booysen

9
É melhor, na minha opinião, permitir que a defaultimplementação de caso retorne me.ToString()e forneça apenas instruções de caso de opção para os valores de enumeração que você deseja substituir. No seu exemplo, entendo que todos são diferentes, mas, nos casos de uso reais, suspeito que a maioria dos valores de enum de palavra única será suficiente e você fornecerá apenas substituições para valores de enum de várias palavras.
5307 Scott

78

Talvez esteja faltando alguma coisa, mas o que há de errado com o Enum.GetName?

public string GetName(PublishStatusses value)
{
    return Enum.GetName(typeof(PublishStatusses), value)
}

edit: para strings amigáveis, você precisa passar por um .resource para concluir a internacionalização / localização, e seria melhor usar uma chave fixa com base na chave enum do que um atributo decorador na mesma.


11
Retorno o valor literal da enumeração, não um valor amigável.
Boris Callens 26/01/09

2
OIC - bem há um muito grande caso que você tem que passar por uma biblioteca de recurso de cadeia com base neste valor, em seguida, porque a alternativa (attribs decorador) não vai apoiar I18N
annakata

1
No caso de I18N, eu faria o método GetDescription () pesquisar na lib de recursos uma string traduzida e retornar à descrição e, em seguida, retornar ao literal.
Boris Callens

3
+1 para MyEnum.ToString () como a chave do recurso para localização. Eu venho fazendo isso há anos
jackvsworld

1
@annakata, na verdade, estendemos o mecanismo de atributo para incluir suporte para l18N; é uma simples mudança de fato.
precisa saber é o seguinte

23

Eu criei um método de extensão reversa para converter a descrição novamente em um valor enum:

public static T ToEnumValue<T>(this string enumerationDescription) where T : struct
{
    var type = typeof(T);

    if (!type.IsEnum)
        throw new ArgumentException("ToEnumValue<T>(): Must be of enum type", "T");

    foreach (object val in System.Enum.GetValues(type))
        if (val.GetDescription<T>() == enumerationDescription)
            return (T)val;

    throw new ArgumentException("ToEnumValue<T>(): Invalid description for enum " + type.Name, "enumerationDescription");
}

15
Sinto muito, mas obrigado por tentar ser útil! Embora por ser um site de perguntas e respostas, as respostas devem ser uma tentativa de responder diretamente à pergunta. E a pergunta afirma especificamente: " Não preciso poder passar da string para o valor novamente " . Mais uma vez, obrigado!
Jesse.

8
Obrigado pela crítica positiva. É sempre difícil ser novo em um site e aprender sobre sua cultura e nuances. Fico feliz que há pessoas como você que definem os novos caras. Mais uma vez, obrigado por não jogar no novo cara.
Brian Richardson

6
@ Jessé E 4 anos depois alguém está feliz em encontrar o código bjrichardson aqui! SO pode ser um site de perguntas e respostas, mas isso não significa que as perguntas terminaram depois de serem respondidas.
John

18

A solução mais fácil aqui é usar um método de extensão personalizado (pelo menos no .NET 3.5 - você pode apenas convertê-lo em um método auxiliar estático para versões anteriores da estrutura).

public static string ToCustomString(this PublishStatusses value)
{
    switch(value)
    {
        // Return string depending on value.
    }
    return null;
}

Estou assumindo aqui que você deseja retornar algo diferente do nome real do valor da enumeração (que você pode obter simplesmente chamando ToString).


Embora válido, eu gosto muito mais do atributo. Dessa forma, eu posso colocar o meu método ToString em uma biblioteca independente, enquanto colocando a representação de seqüência personalizada com a própria enumeração
Boris Callens

1
Justo. Suponho que uma vantagem desse método é que você pode incluir um argumento com o método que especifica alguma variável de estado e, em seguida, alterar qual representação de string é retornada, dependendo disso.
Noldorin

1
Sim, tudo depende do escopo do método, eu acho. Embora a maneira Attribute seja mais genérica, sua solução é mais localizada. No final, trata-se de necessidades.
Boris Callens 26/01/09

1
Você pode colocar métodos de extensão onde quiser. Você apenas precisa fazer referência a onde deseja usá-los.

Sim, mas isso significa que esse método de extensão deve ser reescrito toda vez que você introduzir uma nova enumeração para a qual deseja um nome amigável. Isso também significaria que TODAS as suas aplicações continham nomes amigáveis ​​para todas as suas outras aplicações ...
Boris Callens

13

Esse outro post é Java. Você não pode colocar métodos em enums em c #.

basta fazer algo assim:

PublishStatusses status = ...
String s = status.ToString();

Se você deseja usar valores de exibição diferentes para seus valores de enumeração, use Atributos e Reflexão.


3
toString não é seguro em todos os casos - uma enum com várias entradas com o mesmo valor (digamos, para enumerações inteiras) retornará a chave do primeiro valor correspondente, não a chave do item testado; é por isso que Enum.GetName é preferido
annakata 26/01/09

4
Bem, foi a solução mais fácil para a sua enumeração específica
Lemmy

9

A maneira mais simples é incluir essa classe de extensão em seu projeto, ela funcionará com qualquer enumeração no projeto:

public static class EnumExtensions
{
    public static string ToFriendlyString(this Enum code)
    {
        return Enum.GetName(code.GetType(), code);
    }
}

Uso:

enum ExampleEnum
{
    Demo = 0,
    Test = 1, 
    Live = 2
}

...

ExampleEnum ee = ExampleEnum.Live;
Console.WriteLine(ee.ToFriendlyString());

2
É um mistério o motivo pelo qual esse comentário não é o aceito, ou o mais votado - sem reflexão, sem atributos desnecessários, ideal para situações simples em que o enum já está bem nomeado. Você pode levar esta resposta um pouco mais longe e permitir adicionar espaços entre letras maiúsculas antes de retornar 'My Enum'.
Vix

12
Se o enum já tiver um bom nome, não será necessário nenhum método de extensão. Basta usar o método ToString () existente. string result = "Result: " + ee;
John

Essa deve ser a melhor resposta. Funciona para qualquer enumeração. Você pode até implementá-lo usando Enum específico apenas alterando o tipo de Enum do parâmetro para o Enum real no qual usá-lo.
Juanu Haedo

6
Esta resposta e todos os comentários ignoram a solicitação original para uma descrição estendida. Vocês perderam totalmente o exercício, que é retornar algo diferente do valor padrão do ToString. Não vou reduzir a votação de todas as notas desta resposta aqui, mas com certeza quero.
TonyG

8

Algumas outras opções mais primitivas que evitam classes / tipos de referência:

  • Método de matriz
  • Método struct aninhado

Método de matriz

private struct PublishStatusses
{
    public static string[] Desc = {
        "Not Completed",
        "Completed",
        "Error"
    };

    public enum Id
    {
        NotCompleted = 0,
        Completed,
        Error
    };
}

Uso

string desc = PublishStatusses.Desc[(int)PublishStatusses.Id.Completed];

Método struct aninhado

private struct PublishStatusses
{
    public struct NotCompleted
    {
        public const int Id = 0;
        public const string Desc = "Not Completed";
    }

    public struct Completed
    {
        public const int Id = 1;
        public const string Desc = "Completed";
    }

    public struct Error
    {
        public const int Id = 2;
        public const string Desc = "Error";
    }            
}

Uso

int id = PublishStatusses.NotCompleted.Id;
string desc = PublishStatusses.NotCompleted.Desc;

Atualização (09/09/2018)

Um híbrido de métodos de extensão e a primeira técnica acima.

Prefiro enums a serem definidos onde eles "pertencem" (o mais próximo à fonte de origem e não em algum espaço de nomes global comum).

namespace ViewModels
{
    public class RecordVM
    {
        //public enum Enum { Minutes, Hours }
        public struct Enum
        {
            public enum Id { Minutes, Hours }
            public static string[] Name = { "Minute(s)", "Hour(s)" };
        }
    }
}

O método de extensão parece adequado para uma área comum, e a definição "localizada" do enum agora torna o método de extensão mais detalhado.

namespace Common
{
    public static class EnumExtensions
    {
        public static string Name(this RecordVM.Enum.Id id)
        {
            return RecordVM.Enum.Name[(int)id];
        }
    }   
}

Um exemplo de uso da enumeração e seu método de extensão.

namespace Views
{
    public class RecordView 
    {
        private RecordDataFieldList<string, string> _fieldUnit;

        public RecordView()
        {
            _fieldUnit.List = new IdValueList<string, string>
            {            
                new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()),
                new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name())
            };
        }

        private void Update()
        {    
            RecordVM.Enum.Id eId = DetermineUnit();

            _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value;
        }
    }
}

Nota: Decidi eliminar o Enumwrapper (e a Namematriz), já que é melhor que as cadeias de nomes venham de um recurso (ou seja, arquivo de configuração ou banco de dados), em vez de serem codificadas, e porque acabei colocando o método de extensão no ViewModelsnamespace (apenas em um arquivo "CommonVM.cs" diferente). Além disso, a .Idcoisa toda se torna perturbadora e complicada.

namespace ViewModels
{
    public class RecordVM
    {
        public enum Enum { Minutes, Hours }
        //public struct Enum
        //{
        //    public enum Id { Minutes, Hours }
        //    public static string[] Name = { "Minute(s)", "Hour(s)" };
        //}
    }
}

CommonVM.cs

//namespace Common
namespace ViewModels
{
    public static class EnumExtensions
    {
        public static string Name(this RecordVM.Enum id)
        {
            //return RecordVM.Enum.Name[(int)id];
            switch (id)
            {
                case RecordVM.Enum.Minutes: return "Minute(s)";                    
                case RecordVM.Enum.Hours: return "Hour(s)";
                default: return null;
            }
        }
    }   
}

Um exemplo de uso da enumeração e seu método de extensão.

namespace Views
{
    public class RecordView 
    {
        private RecordDataFieldList<string, string> _fieldUnit

        public RecordView()
        {
            _fieldUnit.List = new IdValueList<string, string>
            {            
                new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()),
                new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name())
            };
        }

        private void Update()
        {    
            RecordVM.Enum eId = DetermineUnit();

            _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value;
        }
    }
}

+ 1-1 = 0 voto: Esta solução preserva a sintaxe do Enum e resolve o problema de maneira elegante sem reflexão ou código complexo, portanto, marque com +1. Mas perde características das próprias Enums. Portanto, embora a IMO seja uma boa opção, ela não responde à pergunta real e recebe -1. Net 0. Desculpe, não temos como gravar isso melhor no SO.
TonyG

@TonyG Fair o suficiente. Depois de perder algumas perguntas sobre a avaliação de habilidades .net do pluarlsight.com, comecei a perceber o quão profundo é o C # enum, por isso é provavelmente uma boa idéia pelo menos conhecer suas capacidades antes de decidir qual metodologia aplicar (especialmente para uso generalizado, refatoração). pode demorar um pouco;).
samis 31/10

7

Você pode usar Humanizer pacote com humanizar Enums possibilidade. Um exemplo:

enum PublishStatusses
{
    [Description("Custom description")]
    NotCompleted,
    AlmostCompleted,
    Error
};

então você pode usar o Humanizemétodo de extensão no enum diretamente:

var st1 = PublishStatusses.NotCompleted;
var str1 = st1.Humanize(); // will result in Custom description

var st2 = PublishStatusses.AlmostCompleted;
var str2 = st2.Humanize(); // will result in Almost completed (calculated automaticaly)

Também usa reflexão e não é armazenado em cache. github.com/Humanizr/Humanizer/blob/…
Konrad

Será tão lento quanto a solução na primeira resposta por Ray
Konrad

5

Com relação a Ray Booysen, há um erro no código: Enum ToString com strings amigáveis

Você precisa levar em consideração vários atributos nos valores da enumeração.

public static string GetDescription<T>(this object enumerationValue)
            where T : struct
    {
        Type type = enumerationValue.GetType();
        if (!type.IsEnum)
        {
            throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
        }

        //Tries to find a DescriptionAttribute for a potential friendly name
        //for the enum
        MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
        if (memberInfo != null && memberInfo.Length > 0)
        {
            object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attrs != null && attrs.Length > 0 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null)
            {
                //Pull out the description value
                return ((DescriptionAttribute)attrs.Where(t=>t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description;
            }
        }
        //If we have no description attribute, just return the ToString of the enum
        return enumerationValue.ToString();

4
A omissão de uma verificação para vários atributos de Descrição é intencional. Se o enum tiver dois e você estiver usando para gerar uma descrição, gostaria de pensar que é uma circunstância excepcional. Eu acho que o bug real é que eu não faço um Single () para ter uma exceção lançada. Caso contrário, toda a assinatura do método não faz sentido. GetDescription ()? Qual descrição? Um agregado?
precisa saber é o seguinte

4
public enum MyEnum
{
    [Description("Option One")]
    Option_One
}

public static string ToDescriptionString(this Enum This)
{
    Type type = This.GetType();

    string name = Enum.GetName(type, This);

    MemberInfo member = type.GetMembers()
        .Where(w => w.Name == name)
        .FirstOrDefault();

    DescriptionAttribute attribute = member != null
        ? member.GetCustomAttributes(true)
            .Where(w => w.GetType() == typeof(DescriptionAttribute))
            .FirstOrDefault() as DescriptionAttribute
        : null;

    return attribute != null ? attribute.Description : name;
}

3
É sempre bom escrever um texto explicando por que isso deve funcionar e por que os OPs não.
Phaberest

Apenas as convenções de código C # da FYI desejam variáveis ​​locais e parâmetros de método com letra inicial em minúscula. Uma exceção é o thisparâmetro nos métodos de extensão, que você pode ver chamado Thisem muitos exemplos na web. Chamá-lo como seu tipo, como você fez ( Enum Enum), torna o código menos legível.
Massimiliano Kraus

4

Em vez de usar um enum, use uma classe estática.

substituir

private enum PublishStatuses{
    NotCompleted,
    Completed,
    Error
};

com

private static class PublishStatuses{
    public static readonly string NotCompleted = "Not Completed";
    public static readonly string Completed = "Completed";
    public static readonly string Error = "Error";
};

será usado assim

PublishStatuses.NotCompleted; // "Not Completed"

Problema usando as principais soluções "método de extensão":

Um enum privado é frequentemente usado dentro de outra classe. A solução do método de extensão não é válida lá, pois deve estar em sua própria classe. Esta solução pode ser privada e incorporada em outra classe.


2

Por acaso, sou fã do VB.NET, então aqui está minha versão, combinando o método DescriptionAttribute com um método de extensão. Primeiro, os resultados:

Imports System.ComponentModel ' For <Description>

Module Module1
  ''' <summary>
  ''' An Enum type with three values and descriptions
  ''' </summary>
  Public Enum EnumType
    <Description("One")>
    V1 = 1

    ' This one has no description
    V2 = 2

    <Description("Three")>
    V3 = 3
  End Enum

  Sub Main()
    ' Description method is an extension in EnumExtensions
    For Each v As EnumType In [Enum].GetValues(GetType(EnumType))
      Console.WriteLine("Enum {0} has value {1} and description {2}",
        v,
        CInt(v),
        v.Description
      )
    Next
    ' Output:
    ' Enum V1 has value 1 and description One
    ' Enum V2 has value 2 and description V2
    ' Enum V3 has value 3 and description Three
  End Sub
End Module

Coisas básicas: uma enumeração chamada EnumType com três valores V1, V2 e V3. A "mágica" acontece na chamada Console.WriteLine em Sub Main (), onde o último argumento é simples v.Description. Isso retorna "Um" para V1, "V2" para V2 e "Três" para V3. Este método de descrição é de fato um método de extensão, definido em outro módulo chamado EnumExtensions:

Option Strict On
Option Explicit On
Option Infer Off

Imports System.Runtime.CompilerServices
Imports System.Reflection
Imports System.ComponentModel

Module EnumExtensions
  Private _Descriptions As New Dictionary(Of String, String)

  ''' <summary>
  ''' This extension method adds a Description method
  ''' to all enum members. The result of the method is the
  ''' value of the Description attribute if present, else
  ''' the normal ToString() representation of the enum value.
  ''' </summary>
  <Extension>
  Public Function Description(e As [Enum]) As String
    ' Get the type of the enum
    Dim enumType As Type = e.GetType()
    ' Get the name of the enum value
    Dim name As String = e.ToString()

    ' Construct a full name for this enum value
    Dim fullName As String = enumType.FullName + "." + name

    ' See if we have looked it up earlier
    Dim enumDescription As String = Nothing
    If _Descriptions.TryGetValue(fullName, enumDescription) Then
      ' Yes we have - return previous value
      Return enumDescription
    End If

    ' Find the value of the Description attribute on this enum value
    Dim members As MemberInfo() = enumType.GetMember(name)
    If members IsNot Nothing AndAlso members.Length > 0 Then
      Dim descriptions() As Object = members(0).GetCustomAttributes(GetType(DescriptionAttribute), False)
      If descriptions IsNot Nothing AndAlso descriptions.Length > 0 Then
        ' Set name to description found
        name = DirectCast(descriptions(0), DescriptionAttribute).Description
      End If
    End If

    ' Save the name in the dictionary:
    _Descriptions.Add(fullName, name)

    ' Return the name
    Return name
  End Function
End Module

Como a pesquisa de atributos de descrição Reflectioné lenta, as pesquisas também são armazenadas em cache de forma privada Dictionary, preenchida por demanda.

(Desculpe pela solução VB.NET - deve ser relativamente simples traduzi-la para C # e meu C # está enferrujado em novos assuntos, como extensões)


2

Resumo limpo das sugestões acima com amostra:

namespace EnumExtensions {

using System;
using System.Reflection;

public class TextAttribute : Attribute {
   public string Text;
   public TextAttribute( string text ) {
      Text = text;
   }//ctor
}// class TextAttribute

public static class EnumExtender {

public static string ToText( this Enum enumeration ) {

   MemberInfo[] memberInfo = enumeration.GetType().GetMember( enumeration.ToString() );

   if ( memberInfo != null && memberInfo.Length > 0 ) {

      object[] attributes = memberInfo[ 0 ].GetCustomAttributes( typeof(TextAttribute),  false );

      if ( attributes != null && attributes.Length > 0 ) {
         return ( (TextAttribute)attributes[ 0 ] ).Text;
      }

   }//if

   return enumeration.ToString();

}//ToText

}//class EnumExtender

}//namespace

USO:

using System;
using EnumExtensions;

class Program {

public enum Appearance {

  [Text( "left-handed" ) ]
  Left,

  [Text( "right-handed" ) ]
  Right,

}//enum

static void Main( string[] args ) {

   var appearance = Appearance.Left;
   Console.WriteLine( appearance.ToText() );

}//Main

}//class

1
Há um atributo [Description ("")]) em C #, por que não usar isso?
Stefan Koenen

É claro que usar [Descrição ("")]) é um caminho a percorrer. Mas eu queria que a amostra estivesse completa.
Sublinhado

2

Use Enum.GetName

No link acima ...

using System;

public class GetNameTest {
    enum Colors { Red, Green, Blue, Yellow };
    enum Styles { Plaid, Striped, Tartan, Corduroy };

    public static void Main() {

        Console.WriteLine("The 4th value of the Colors Enum is {0}", Enum.GetName(typeof(Colors), 3));
        Console.WriteLine("The 4th value of the Styles Enum is {0}", Enum.GetName(typeof(Styles), 3));
    }
}
// The example displays the following output:
//       The 4th value of the Colors Enum is Yellow
//       The 4th value of the Styles Enum is Corduroy


1

Esta é uma atualização do código de Ray Booysen que usa o método GetCustomAttributes genérico e o LINQ para tornar as coisas um pouco mais organizadas.

    /// <summary>
    /// Gets the value of the <see cref="T:System.ComponentModel.DescriptionAttribute"/> on an struct, including enums.  
    /// </summary>
    /// <typeparam name="T">The type of the struct.</typeparam>
    /// <param name="enumerationValue">A value of type <see cref="T:System.Enum"/></param>
    /// <returns>If the struct has a Description attribute, this method returns the description.  Otherwise it just calls ToString() on the struct.</returns>
    /// <remarks>Based on http://stackoverflow.com/questions/479410/enum-tostring/479417#479417, but useful for any struct.</remarks>
    public static string GetDescription<T>(this T enumerationValue) where T : struct
    {
        return enumerationValue.GetType().GetMember(enumerationValue.ToString())
                .SelectMany(mi => mi.GetCustomAttributes<DescriptionAttribute>(false),
                    (mi, ca) => ca.Description)
                .FirstOrDefault() ?? enumerationValue.ToString();
    }   

Não conseguiu entender por que você precisa ser genérico? Se você vai usar reflexão?
Lee Louviere

@LeeLouviere Principalmente para evitar o boxe quando a estrutura (tipo de valor) é passada como parâmetro.
Richard Anthony Hein

1
em vez disso, numerationValue.GetType () use: typeof (T).
Slava

1
Grande melhoria de uma linha em relação à resposta aceita sem (YMMV) perder a legibilidade. Sim, com typeof (T).
TonyG

1

Resumo ainda mais limpo:

using System;
using System.Reflection;

public class TextAttribute : Attribute
{
    public string Text;
    public TextAttribute(string text)
    {
        Text = text;
    }
}  

public static class EnumExtender
{
    public static string ToText(this Enum enumeration)
    {
        var memberInfo = enumeration.GetType().GetMember(enumeration.ToString());
        if (memberInfo.Length <= 0) return enumeration.ToString();

        var attributes = memberInfo[0].GetCustomAttributes(typeof(TextAttribute), false);
        return attributes.Length > 0 ? ((TextAttribute)attributes[0]).Text : enumeration.ToString();
    }
}

O mesmo uso que o sublinhado descreve.


0

Para sinalizadores enum incluindo.

    public static string Description(this Enum value)
    {
        Type type = value.GetType();

        List<string> res = new List<string>();
        var arrValue = value.ToString().Split(',').Select(v=>v.Trim());
        foreach (string strValue in arrValue)
        {
            MemberInfo[] memberInfo = type.GetMember(strValue);
            if (memberInfo != null && memberInfo.Length > 0)
            {
                object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (attrs != null && attrs.Length > 0 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null)
                {
                    res.Add(((DescriptionAttribute)attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description);
                }
                else
                    res.Add(strValue);
            }
            else
                res.Add(strValue);
        }

        return res.Aggregate((s,v)=>s+", "+v);
    }

0

Caso você queira apenas adicionar um espaço em branco entre as palavras, é tão simples quanto

string res = Regex.Replace(PublishStatusses.NotCompleted, "[A-Z]", " $0").Trim();

0

Eu uso uma classe genérica para armazenar os pares enum / description e uma classe auxiliar aninhada para obter a descrição.

O enum :

enum Status { Success, Fail, Pending }

A classe genérica:

Nota: Como uma classe genérica não pode ser restringida por uma enumeração , estou restringindo por struct e verificando a enumeração no construtor.

public class EnumX<T> where T : struct
{
    public T Code { get; set; }
    public string Description { get; set; }

    public EnumX(T code, string desc)
    {
        if (!typeof(T).IsEnum) throw new NotImplementedException();

        Code = code;
        Description = desc;
    }

    public class Helper
    {
        private List<EnumX<T>> codes;

        public Helper(List<EnumX<T>> codes)
        {
            this.codes = codes;
        }

        public string GetDescription(T code)
        {
            EnumX<T> e = codes.Where(c => c.Code.Equals(code)).FirstOrDefault();
            return e is null ? "Undefined" : e.Description;
        }
    }
}

Uso:

EnumX<Status>.Helper StatusCodes = new EnumX<Status>.Helper(new List<EnumX<Status>>()
        {
            new EnumX<Status>(Status.Success,"Operation was successful"),
            new EnumX<Status>(Status.Fail,"Operation failed"),
            new EnumX<Status>(Status.Pending,"Operation not complete. Please wait...")
        });

        Console.WriteLine(StatusCodes.GetDescription(Status.Pending));

-2

Eu acho que a melhor (e mais fácil) maneira de resolver seu problema é escrever um método de extensão para sua enumeração:

public static string GetUserFriendlyString(this PublishStatusses status)
    {

    }

1
Alguém foi sete anos antes para afirmar que
Steven

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.