Enum ToString con cadenas fáciles de usar

282

Mi enumeración consta de los siguientes valores:

private enum PublishStatusses{
    NotCompleted,
    Completed,
    Error
};

Sin embargo, quiero poder generar estos valores de una manera fácil de usar.
No necesito poder pasar de cadena a valor nuevamente.

Boris Callens
fuente
posible duplicado de enumeraciones
nawfal
Posible duplicado de la representación
Liam

Respuestas:

350

Yo uso el Descriptionatributo del espacio de nombres System.ComponentModel. Simplemente decora la enumeración:

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

Luego use este código para recuperarlo:

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();
}
Ray Booysen
fuente
12
Este ejemplo es más fácil de leer. stackoverflow.com/questions/1415140/…
RayLoveless
31
Sospecho que hay un impacto significativo en el rendimiento para usar la reflexión como se describe en esta solución. El código para el método de Will de usar el método de extensión ToFriendlyString es mucho más fácil de entender, y su rendimiento también debería ser extremadamente rápido.
humbads
1
Me gusta la versión que @RayL enlazó, ya que solo agregará el método de extensión a Enums. Si eso es todo lo que desea utilizar esto para (como se indica con el ArgumentException, entonces no hay razón para que el método sea totalmente genérico.
krillgar
44
Significa que cada enumeración necesita su propio método de extensión. Este es un uso más general y requiere más trabajo, pero es probable que desee cuantificar lo que significa "rápido" antes de decidir sobre el rendimiento.
Ray Booysen
2
@petar que funciona, pero no si desea que se muestren cadenas amigables a los usuarios. MY_TYPE tendrá el guión bajo y no se puede personalizar.
Ray Booysen
354

Hago esto con métodos de extensión:

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

fuente
66
Esto es mucho más limpio que la respuesta del atributo. ¡Agradable!
pennyrave
3
@pennyrave: Eh. Muchos componentes de la interfaz de usuario esperan encontrar y usar DisplayNameAttribute y DescriptionAttribute. De hecho, ahora, uso estos y un método de extensión para obtener fácilmente esos valores.
6060
El problema que veo con esto es que constantemente estás escribiendo estos métodos de extensión. Con el mecanismo de atributo, es una forma simple de decorarlo y solo llamar a un método.
Ray Booysen
55
¿No estoy seguro de lo que quieres decir?
Ray Booysen
9
Es mejor, en mi opinión, permitir que la defaultimplementación del caso regrese me.ToString()y solo proporcionar declaraciones de casos de cambio para los valores de enumeración que desea anular. En su ejemplo, entiendo que todos son diferentes, pero en casos de uso real, sospecho que la mayoría de los valores de enumeración de una sola palabra serán suficientes y solo proporcionará anulaciones para los valores de enumeración de varias palabras.
Scott
78

Tal vez me estoy perdiendo algo, pero ¿qué hay de malo en Enum.GetName?

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

editar: para cadenas fáciles de usar, debe pasar por un recurso .resource para realizar la internacionalización / localización, y podría decirse que sería mejor usar una clave fija basada en la clave enum que un atributo decorador en la misma.

annakata
fuente
11
Devuelvo el valor literal de la enumeración, no uno fácil de usar.
Boris Callens
2
oic - bueno, hay un caso bastante grande en el que tienes que pasar por una biblioteca de recursos de cadena basada en este valor, porque la alternativa (decorator attribs) no admitirá I18N
annakata
1
En el caso de I18N, haría que el método GetDescription () buscara en la biblioteca de recursos una cadena traducida y volviera a la descripción y luego volviera al literal.
Boris Callens
3
+1 para MyEnum.ToString () como la clave de recursos para la localización. He estado haciendo eso durante años
jackvsworld
1
@annakata en realidad hemos extendido el mecanismo de atributos para incluir soporte para l18N, de hecho es un cambio simple.
Ray Booysen
23

Creé un método de extensión inversa para convertir la descripción nuevamente en un valor de enumeración:

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");
}
Brian Richardson
fuente
15
Lo siento, pero gracias por tratar de ser útil. Aunque debido a que este es un sitio de preguntas y respuestas, las respuestas deben ser un intento de responder directamente a la pregunta. Y la pregunta dice específicamente: " No necesito volver a pasar de cadena a valor " . Una vez más, ¡gracias!
Jesse
8
Gracias por las críticas positivas. Siempre es difícil ser nuevo en un sitio y aprender sobre su cultura y sus matices. Me alegra que haya personas como tú que aclaren a los nuevos. Una vez más, gracias por no tirar al nuevo chico.
Brian Richardson
66
@Jesse ¡Y 4 años después, alguien está feliz de encontrar el código bjrichardson aquí! SO podría ser un sitio de preguntas y respuestas, pero no significa que las preguntas estén muertas después de ser respondidas.
John
18

La solución más fácil aquí es utilizar un método de extensión personalizado (al menos en .NET 3.5, simplemente puede convertirlo en un método auxiliar estático para versiones anteriores de framework).

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

Supongo aquí que desea devolver algo más que el nombre real del valor de enumeración (que puede obtener simplemente llamando a ToString).

Noldorin
fuente
Aunque es válido, me gusta mucho más el atributo. De esa manera puedo poner mi método toString en una biblioteca independiente, mientras que poner la representación de cadena personalizada con la enumeración en sí
Boris Callens
1
Lo suficientemente justo. Supongo que una ventaja de este método es que puede incluir un argumento con el método que especifica alguna variable de estado, y luego cambiar qué representación de cadena se devuelve dependiendo de esto.
Noldorin
1
Sí, todo depende del alcance del método, supongo. Si bien la forma de Atributo es más genérica, su solución está más localizada. Al final, todo se trata de necesidades.
Boris Callens
1
Puede colocar métodos de extensión en cualquier lugar que desee. Solo tiene que hacer referencia a donde desea usarlos.
Sí, pero esto significaría que este método de extensión debe reescribirse cada vez que introduce una nueva enumeración para la que desea tener un nombre descriptivo. Esto también significaría que todas sus aplicaciones llevarían nombres alrededor ambiente para todas sus otras aplicaciones ...
Boris Callens
13

Esa otra publicación es Java. No puede poner métodos en Enums en C #.

solo haz algo como esto:

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

Si desea usar diferentes valores de visualización para sus valores de enumeración, puede usar Atributos y Reflexión.

Lemmy
fuente
3
toString no es seguro en todos los casos: una enumeración con múltiples entradas con el mismo valor (digamos para enumeraciones enteras) devolverá la clave del primer valor coincidente, no la clave del elemento probado, es por eso que se prefiere Enum.GetName
annakata
44
Bueno, fue la solución más fácil para su enumeración específica
Lemmy el
9

La forma más simple es incluir esta clase de extensión en su proyecto, funcionará con cualquier enumeración en el proyecto:

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());
Milan Švec
fuente
2
Es un misterio por qué este comentario no es el aceptado o el más votado: sin reflexión, sin atributos innecesarios, ideal para situaciones simples donde la enumeración ya está bien nombrada. Podría llevar esta respuesta un paso más allá y permitir agregar espacios entre mayúsculas antes de regresar, 'Mi enumeración'.
Vix
12
Si la enumeración ya está bien nombrada, no hay necesidad de ningún método de extensión. Simplemente use el método ToString () existente. string result = "Result: " + ee;
John
Esta debería ser la mejor respuesta. Funciona para cualquier enumeración. Incluso podría implementarlo usando Enum específico simplemente cambiando el tipo de Enum del parámetro al Enum real en el que usarlo.
Juanu Haedo
66
Esta respuesta y todos los comentarios ignoran la solicitud original de una descripción ampliada. Ustedes perdieron totalmente el ejercicio que consiste en devolver algo distinto del valor predeterminado de ToString. No rechazaré todas las notas para esta respuesta aquí, pero seguro que quiero hacerlo.
TonyG
8

Algunas otras opciones más primitivas que evitan clases / tipos de referencia:

  • Método de matriz
  • Método de estructura anidada

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 de estructura anidada

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;

Actualización (03/09/2018)

Un híbrido de métodos de extensión y la primera técnica anterior.

Prefiero que las enumeraciones se definan donde "pertenecen" (más cercanas a su fuente de origen y no en un espacio de nombres global común).

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

El método de extensión parece adecuado para un área común, y la definición "localizada" de la enumeración ahora hace que el método de extensión sea más detallado.

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

Un ejemplo de uso de la enumeración y su método de extensión.

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: en realidad decidí eliminar el Enumcontenedor (y la Namematriz), ya que es mejor que las cadenas de nombre provengan de un recurso (es decir, un archivo de configuración o DB) en lugar de estar codificadas, y porque terminé poniendo el método de extensión en el ViewModelsespacio de nombres (solo en un archivo diferente, "CommonVM.cs"). Además, todo se .Idvuelve molesto y molesto.

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

Un ejemplo de uso de la enumeración y su método de extensión.

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;
        }
    }
}
Sam es
fuente
+ 1-1 = 0 voto: esta solución conserva la sintaxis de Enum y resuelve el problema de manera elegante sin reflexión o código complejo, por lo que +1 está ahí. Pero pierde características de los Enums mismos. Entonces, aunque IMO es una buena opción, no responde la pregunta real y obtiene un -1. Net 0. Lo siento, no tenemos una manera de grabar eso mejor en SO.
TonyG
@TonyG Justo lo suficiente. Después de perder algunas preguntas sobre la evaluación de habilidades .net de pluarlsight.com, comencé a darme cuenta de cuán en profundidad son las enumeraciones de C #, por lo que probablemente sea una buena idea al menos conocer sus capacidades antes de decidir qué metodología aplicar (especialmente para el uso generalizado, la refactorización puede ser un poco de tiempo;).
samis
7

Puede utilizar Humanizer paquete con Humanize enumeraciones posibilidad. Un ejemplo:

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

entonces puede usar el Humanizemétodo de extensión en enum directamente:

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)
Konrad Kokosa
fuente
Utiliza la reflexión también y no está en caché. github.com/Humanizr/Humanizer/blob/…
Konrad
Será tan lento como la solución en la primera respuesta de Ray
Konrad
5

Con respecto a Ray Booysen, hay un error en el código: Enum ToString con cadenas fáciles de usar

Debe tener en cuenta varios atributos en los valores de enumeración.

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();
Joel MC
fuente
44
La omisión de una verificación para múltiples atributos de Descripción es a propósito. Si la enumeración tiene dos, y está utilizando para generar una descripción, me gustaría pensar que es una circunstancia excepcional. Creo que el error real es que no hago un Single () para lanzar una excepción. De lo contrario, toda la firma del método no tiene sentido. GetDescription ()? Cual descripcion? Un agregado?
Ray Booysen
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;
}
Diogo Freitas
fuente
3
Siempre es bueno escribir un texto que explique por qué esto debería funcionar y por qué los OP no.
phaberest
Solo para su información, las convenciones de código C # quieren variables locales y parámetros de método con letra inicial en minúscula. Una excepción es el thisparámetro en los métodos de extensión, que puede ver llamado Thisen muchos ejemplos en la web. Llamarlo como su tipo como lo hiciste ( Enum Enum) hace que el código sea menos legible.
Massimiliano Kraus
4

En lugar de usar una enumeración, use una clase estática.

reemplazar

private enum PublishStatuses{
    NotCompleted,
    Completed,
    Error
};

con

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

se usará así

PublishStatuses.NotCompleted; // "Not Completed"

Problema con las principales soluciones de "método de extensión":

Una enumeración privada a menudo se usa dentro de otra clase. La solución del método de extensión no es válida allí ya que debe estar en su propia clase. Esta solución puede ser privada e incrustada en otra clase.

Guirnalda de asher
fuente
2

Resulta que soy un fanático de VB.NET, así que aquí está mi versión, combinando el método DescripciónAtributo con un método de extensión. Primero, los 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

Material básico: una enumeración llamada EnumType con tres valores V1, V2 y V3. La "magia" ocurre en la llamada Console.WriteLine en Sub Main (), donde el último argumento es simple v.Description. Esto devuelve "Uno" para V1, "V2" para V2 y "Tres" para V3. Este método de descripción es, de hecho, un método de extensión, definido en otro módulo llamado 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

Debido a que la búsqueda de atributos de descripción Reflectiones lenta, las búsquedas también se almacenan en caché en privadoDictionary , que se completa bajo demanda.

(Perdón por la solución VB.NET: debería ser relativamente sencillo traducirlo a C #, y mi C # está oxidado en temas nuevos como extensiones)

MarkusT
fuente
2

Resumen limpio de las sugerencias anteriores con muestra:

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
guion bajo
fuente
1
Hay un atributo [Descripción ("")] en C #, ¿por qué no usarlo?
Stefan Koenen
Por supuesto, usar [Descripción ("")] es un camino a seguir. Pero quería que la muestra estuviera completa.
subrayar
2

Utilice Enum.GetName

Desde el enlace de arriba ...

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
JMH
fuente
1

Esta es una actualización del código de Ray Booysen que usa el método genérico GetCustomAttributes y LINQ para hacer las cosas un poco más ordenadas.

    /// <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();
    }   
Richard Anthony Hein
fuente
¿No puede ver por qué necesita que sea genérico? Si vas a usar la reflexión?
Lee Louviere
@LeeLouviere Principalmente para evitar el boxeo cuando la estructura (tipo de valor) se pasa como parámetro.
Richard Anthony Hein
1
en lugar de numerationValue.GetType () use: typeof (T).
Slava
1
Enorme mejora de una línea sobre la respuesta aceptada sin que (YMMV) pierda legibilidad. Sí, con typeof (T).
TonyG
1

Resumen aún más limpio:

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

El mismo uso que el subrayado describe.

StefanDK
fuente
0

Para banderas enum incluidas.

    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);
    }
Lukich
fuente
0

En caso de que solo desee agregar un espacio en blanco entre las palabras, es tan simple como

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

Utilizo una clase genérica para almacenar los pares enum / description y una clase auxiliar anidada para obtener la descripción.

La enumeración :

enum Status { Success, Fail, Pending }

La clase genérica:

Nota: Dado que una clase genérica no puede estar limitada por una enumeración , estoy restringiendo por estructura en su lugar y comprobando la enumeración en el constructor.

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));
zisha
fuente
-2

Creo que la mejor (y más fácil) forma de resolver su problema es escribir un Método de extensión para su enumeración:

public static string GetUserFriendlyString(this PublishStatusses status)
    {

    }
usuario7096584
fuente
1
Alguien tenía 7 años antes para decir eso
Steven