Enum ToString com cordas de fácil utilização
Pergunta
O meu enum é composto dos seguintes valores:
private enum PublishStatusses{
NotCompleted,
Completed,
Error
};
Eu quero ser capaz de saída esses valores em uma maneira amigável embora.
Eu não preciso de ser capaz de ir de string para valor novamente.
Solução
Eu uso o href="http://msdn.microsoft.com/en-us/library/system.componentmodel.descriptionattribute.aspx" rel="noreferrer"> Description
atributo do Sistema namespace .ComponentModel. Simplesmente decorar a enumeração:
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();
}
Outras dicas
Eu faço isso com os 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!";
}
}
}
Talvez eu estou faltando alguma coisa, mas o que há de errado com Enum.GetName?
public string GetName(PublishStatusses value)
{
return Enum.GetName(typeof(PublishStatusses), value)
}
edit: para cordas user-friendly, você precisa passar por um .resource para obter internacionalização / localização feito, e seria sem dúvida ser melhor usar uma chave fixa com base na chave de enumeração do que um atributo decorador sobre o mesmo.
Eu criei um método de extensão inversa para converter a descrição de volta para um valor de enumeração:
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");
}
A solução mais fácil é usar um método de extensão personalizada (em .NET 3.5 pelo menos - você pode simplesmente convertê-lo em um método auxiliar estática para versões quadro anteriores).
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 de enumeração (que você pode obter simplesmente chamando ToString).
O outro post é Java. Você não pode colocar métodos em enumerações em C #.
apenas fazer algo como isto:
PublishStatusses status = ...
String s = status.ToString();
Se você quiser usar diferentes valores de exibição para seus valores enum, você poderia usar Atributos e Reflexão.
A maneira mais simples é apenas para incluir esta classe de extensão em seu projeto, ele vai trabalhar com qualquer enum 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());
Algumas outras opções mais primitivos que os tipos de aulas Evitar / referência:
- método array
- método struct Nested
método array
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 Nested
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;
Update (2018/03/09)
Um híbrido de Métodos de extensão e o primeiro técnica acima.
Eu prefiro enums a definir onde eles "pertencem" (mais próximo à sua fonte de origem e não em algum comum, namespace global).
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, eo "localizada" definição da enumeração agora faz com que o método de extensão mais detalhada.
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 do enum e do 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: eu realmente decidiu eliminar o wrapper Enum
(e variedade Name
), já que é melhor que as seqüências de nome vem de um recurso (arquivo ou seja config ou DB) em vez de ser codificado , e porque eu acabei colocando o método de extensão no namespace ViewModels
(just in, um arquivo "CommonVM.cs" diferente). Além disso, a coisa toda .Id
torna-se distrair e pesado.
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 do enum e do 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;
}
}
}
Você pode usar pacote Humanizer com humanizar enums possibilidade. Um eaxample:
enum PublishStatusses
{
[Description("Custom description")]
NotCompleted,
AlmostCompleted,
Error
};
então você pode usar o método de extensão Humanize
em 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)
Com relação a Ray Booysen, há um bug no código: Enum ToString com cordas de fácil utilização
Você precisa de conta para vários atributos sobre os valores enum.
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();
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;
}
Em vez de usar um uso enum 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";
};
que será usado como este
PublishStatuses.NotCompleted; // "Not Completed"
Issue usando o top "método de extensão" soluções:
A enumeração privada é muitas vezes usado dentro de outra classe. A solução método de extensão não é válido lá desde que deve estar em sua própria classe. Esta solução pode ser privado e incorporado em outra classe.
Acontece que eu sou um fã VB.NET, então aqui está a minha versão, combinando o método DescriptionAttribute com um método de extensão. Em primeiro lugar, 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
material básico: um enum chamado enumType com três valores V1, V2 e V3. A "mágica" acontece na chamada Console.WriteLine em Sub Main (), onde o último argumento é simplesmente v.Description
. Isso retorna "um" para V1, "V2" para V2, e "Três" para a V3. Description-método é na verdade um método de extensão, definida 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
Porque olhando para cima descrição atributos usando Reflection
é lento, as pesquisas também são armazenados em cache em um Dictionary
privada, que é preenchido on demand.
(Desculpe para a solução VB.NET - deve ser relativamente de maneira direta para traduzi-lo para C #, e meu C # é oxidada em novos temas como extensões)
Limpo resumo das sugestões acima com a 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
Esta é uma atualização do código de Ray Booysen que usa o método GetCustomAttributes genéricos e LINQ para tornar as coisas um pouco mais arrumado.
/// <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();
}
resumo de mesmo:
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();
}
}
mesmo uso como sublinhado descreve.
Use Enum.GetName
A partir do 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
Para sinalizadores enum inclusive.
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);
}
Eu acho que o melhor (e mais fácil) maneira de resolver o seu problema é escrever um Extension-Método para sua enumeração:
public static string GetUserFriendlyString(this PublishStatusses status)
{
}
Se você quiser algo completamente customizável, experimentar a minha solução aqui:
http : //www.kevinwilliampang.com/post/Mapping-Enums-To-Strings-and-Strings-to-Enums-in-NET.aspx
Basicamente, os contornos Post Como para anexar Descrição atribui a cada um dos seus enums e fornece uma maneira genérica para mapear enum a descrição.