c# - getvalues - enum type
RepresentaciĆ³n de cadena de un Enum (30)
Tengo la siguiente enumeración:
public enum AuthenticationMethod
{
FORMS = 1,
WINDOWSAUTHENTICATION = 2,
SINGLESIGNON = 3
}
Sin embargo, el problema es que necesito la palabra "FORMAS" cuando pido AuthenticationMethod.FORMS y no el ID 1.
He encontrado la siguiente solución para este problema ( link ):
Primero necesito crear un atributo personalizado llamado "StringValue":
public class StringValue : System.Attribute
{
private readonly string _value;
public StringValue(string value)
{
_value = value;
}
public string Value
{
get { return _value; }
}
}
Entonces puedo agregar este atributo a mi enumerador:
public enum AuthenticationMethod
{
[StringValue("FORMS")]
FORMS = 1,
[StringValue("WINDOWS")]
WINDOWSAUTHENTICATION = 2,
[StringValue("SSO")]
SINGLESIGNON = 3
}
Y, por supuesto, necesito algo para recuperar ese StringValue:
public static class StringEnum
{
public static string GetStringValue(Enum value)
{
string output = null;
Type type = value.GetType();
//Check first in our cached results...
//Look for our ''StringValueAttribute''
//in the field''s custom attributes
FieldInfo fi = type.GetField(value.ToString());
StringValue[] attrs =
fi.GetCustomAttributes(typeof(StringValue),
false) as StringValue[];
if (attrs.Length > 0)
{
output = attrs[0].Value;
}
return output;
}
}
Bien, ahora tengo las herramientas para obtener un valor de cadena para un enumerador. Entonces puedo usarlo así:
string valueOfAuthenticationMethod = StringEnum.GetStringValue(AuthenticationMethod.FORMS);
Bueno, ahora todo esto funciona como un encanto, pero me parece mucho trabajo. Me preguntaba si hay una mejor solución para esto.
También probé algo con un diccionario y propiedades estáticas, pero eso tampoco fue mejor.
Cómo resolví esto como un método de extensión:
using System.ComponentModel;
public static string GetDescription(this Enum value)
{
var descriptionAttribute = (DescriptionAttribute)value.GetType()
.GetField(value.ToString())
.GetCustomAttributes(false)
.Where(a => a is DescriptionAttribute)
.FirstOrDefault();
return descriptionAttribute != null ? descriptionAttribute.Description : value.ToString();
}
Enumerar
public enum OrderType
{
None = 0,
[Description("New Card")]
NewCard = 1,
[Description("Reload")]
Refill = 2
}
Uso (donde o.OrderType es una propiedad con el mismo nombre que la enumeración):
o.OrderType.GetDescription()
Lo que me da una cadena de "Nueva tarjeta" o "Recargar" en lugar del valor de enumeración real NewCard y Recarga.
Como la mayoría de ustedes, me gustó mucho la respuesta seleccionada de Jakub Šturc , pero también odio copiar y pegar el código, y trato de hacerlo lo menos posible.
Así que decidí que quería una clase EnumBase de la que se hereda / incorpore la mayor parte de la funcionalidad, lo que me permite centrarme en el contenido en lugar del comportamiento.
El problema principal con este enfoque se basa en el hecho de que, aunque los valores de Enum son instancias seguras para el tipo, la interacción es con la implementación estática del tipo de Clase de Enum. Así que con un poco de magia genérica, creo que finalmente obtuve la combinación correcta. Espero que alguien encuentre esto tan útil como yo lo hice.
Comenzaré con el ejemplo de Jakub, pero usando herencia y genéricos:
public sealed class AuthenticationMethod : EnumBase<AuthenticationMethod, int>
{
public static readonly AuthenticationMethod FORMS =
new AuthenticationMethod(1, "FORMS");
public static readonly AuthenticationMethod WINDOWSAUTHENTICATION =
new AuthenticationMethod(2, "WINDOWS");
public static readonly AuthenticationMethod SINGLESIGNON =
new AuthenticationMethod(3, "SSN");
private AuthenticationMethod(int Value, String Name)
: base( Value, Name ) { }
public new static IEnumerable<AuthenticationMethod> All
{ get { return EnumBase<AuthenticationMethod, int>.All; } }
public static explicit operator AuthenticationMethod(string str)
{ return Parse(str); }
}
Y aquí está la clase base:
using System;
using System.Collections.Generic;
using System.Linq; // for the .AsEnumerable() method call
// E is the derived type-safe-enum class
// - this allows all static members to be truly unique to the specific
// derived class
public class EnumBase<E, T> where E: EnumBase<E, T>
{
#region Instance code
public T Value { get; private set; }
public string Name { get; private set; }
protected EnumBase(T EnumValue, string Name)
{
Value = EnumValue;
this.Name = Name;
mapping.Add(Name, this);
}
public override string ToString() { return Name; }
#endregion
#region Static tools
static private readonly Dictionary<string, EnumBase<E, T>> mapping;
static EnumBase() { mapping = new Dictionary<string, EnumBase<E, T>>(); }
protected static E Parse(string name)
{
EnumBase<E, T> result;
if (mapping.TryGetValue(name, out result))
{
return (E)result;
}
throw new InvalidCastException();
}
// This is protected to force the child class to expose it''s own static
// method.
// By recreating this static method at the derived class, static
// initialization will be explicit, promising the mapping dictionary
// will never be empty when this method is called.
protected static IEnumerable<E> All
{ get { return mapping.Values.AsEnumerable().Cast<E>(); } }
#endregion
}
Cuando me enfrento a este problema, hay un par de preguntas a las que trato de encontrar las respuestas primero:
- ¿Son los nombres de mis valores de enumeración suficientemente amigables para el propósito, o debo proporcionar los más amigables?
- ¿Necesito ida y vuelta? Es decir, ¿necesitaré tomar valores de texto y analizarlos en valores de enumeración?
- ¿Es esto algo que necesito hacer para muchas enumeraciones en mi proyecto, o solo una?
- ¿En qué tipo de elementos de la interfaz de usuario presentaré esta información, en particular, estaré vinculado a la interfaz de usuario o utilizando hojas de propiedades?
- ¿Esto necesita ser localizable?
La forma más sencilla de hacerlo es con Enum.GetValue
(y es compatible con el disparo de ida y vuelta utilizando Enum.Parse
). También a menudo vale la pena construir un TypeConverter
, como sugiere Steve Mitcham, para admitir el enlace de UI. (No es necesario crear un TypeConverter
cuando está utilizando hojas de propiedades, que es una de las cosas buenas de las hojas de propiedades. Aunque Lord sabe que tienen sus propios problemas).
En general, si las respuestas a las preguntas anteriores sugieren que no va a funcionar, mi siguiente paso es crear y rellenar un Dictionary<MyEnum, string>
estático Dictionary<MyEnum, string>
, o posiblemente un Dictionary<Type, Dictionary<int, string>>
. Tiendo a omitir el paso intermedio de decorar el código con atributos porque lo que generalmente viene a continuación es la necesidad de cambiar los valores amigables después del despliegue (a menudo, pero no siempre, debido a la localización).
Desafortunadamente, la reflexión para obtener atributos en enumeraciones es bastante lenta:
Vea esta pregunta: ¿ Alguien sabe una forma rápida de obtener atributos personalizados en un valor de enumeración?
El .ToString()
es bastante lento en las enumeraciones también.
Puedes escribir métodos de extensión para enumeraciones aunque:
public static string GetName( this MyEnum input ) {
switch ( input ) {
case MyEnum.WINDOWSAUTHENTICATION:
return "Windows";
//and so on
}
}
Esto no es excelente, pero será rápido y no requerirá la reflexión para los atributos o el nombre del campo.
Actualización C # 6
Si puede usar C # 6, entonces el nuevo operador nameof
funciona para enumeraciones, por lo que nameof(MyEnum.WINDOWSAUTHENTICATION)
se convertirá a "WINDOWSAUTHENTICATION"
en el momento de la compilación , por lo que es la forma más rápida de obtener nombres de enumeración.
Tenga en cuenta que esto convertirá la enumeración explícita en una constante en línea, por lo que no funciona para las enumeraciones que tiene en una variable. Asi que:
nameof(AuthenticationMethod.FORMS) == "FORMS"
Pero...
var myMethod = AuthenticationMethod.FORMS;
nameof(myMethod) == "myMethod"
En su pregunta nunca dijo que realmente necesita el valor numérico de la enumeración en cualquier lugar.
Si no lo hace y solo necesita una enumeración de tipo cadena (que no es un tipo integral, por lo que no puede ser una base de enumeración), aquí tiene una forma:
static class AuthenticationMethod
{
public static readonly string
FORMS = "Forms",
WINDOWSAUTHENTICATION = "WindowsAuthentication";
}
puede utilizar la misma sintaxis que enum para hacer referencia a ella
if (bla == AuthenticationMethod.FORMS)
Será un poco más lento que con los valores numéricos (comparando cadenas en lugar de números), pero en el lado positivo no está utilizando la reflexión (lento) para acceder a la cadena.
Método de uso
Enum.GetName(Type MyEnumType, object enumvariable)
como en (Asume que el Shipper
es un Enum definido)
Shipper x = Shipper.FederalExpress;
string s = Enum.GetName(typeof(Shipper), x);
Hay un montón de otros métodos estáticos en la clase Enum que vale la pena investigar también ...
Mi variante
public struct Colors
{
private String current;
private static string red = "#ff0000";
private static string green = "#00ff00";
private static string blue = "#0000ff";
private static IList<String> possibleColors;
public static Colors Red { get { return (Colors) red; } }
public static Colors Green { get { return (Colors) green; } }
public static Colors Blue { get { return (Colors) blue; } }
static Colors()
{
possibleColors = new List<string>() {red, green, blue};
}
public static explicit operator String(Colors value)
{
return value.current;
}
public static explicit operator Colors(String value)
{
if (!possibleColors.Contains(value))
{
throw new InvalidCastException();
}
Colors color = new Colors();
color.current = value;
return color;
}
public static bool operator ==(Colors left, Colors right)
{
return left.current == right.current;
}
public static bool operator !=(Colors left, Colors right)
{
return left.current != right.current;
}
public bool Equals(Colors other)
{
return Equals(other.current, current);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (obj.GetType() != typeof(Colors)) return false;
return Equals((Colors)obj);
}
public override int GetHashCode()
{
return (current != null ? current.GetHashCode() : 0);
}
public override string ToString()
{
return current;
}
}
El código parece un poco feo, pero los usos de esta estructura son bastante presentativos.
Colors color1 = Colors.Red;
Console.WriteLine(color1); // #ff0000
Colors color2 = (Colors) "#00ff00";
Console.WriteLine(color2); // #00ff00
// Colors color3 = "#0000ff"; // Compilation error
// String color4 = Colors.Red; // Compilation error
Colors color5 = (Colors)"#ff0000";
Console.WriteLine(color1 == color5); // True
Colors color6 = (Colors)"#00ff00";
Console.WriteLine(color1 == color6); // False
Además, creo que, si se requiere una gran cantidad de tales enumeraciones, se puede usar la generación de código (por ejemplo, T4).
Pruebe el type-safe-enum patrón de type-safe-enum .
public sealed class AuthenticationMethod {
private readonly String name;
private readonly int value;
public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (1, "FORMS");
public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (2, "WINDOWS");
public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (3, "SSN");
private AuthenticationMethod(int value, String name){
this.name = name;
this.value = value;
}
public override String ToString(){
return name;
}
}
Actualización de tipo explícita (o implícita) la conversión se puede hacer por
añadiendo campo estático con mapeo
private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
- nb Para que la inicialización de los campos del "miembro de enumeración" no arroje una NullReferenceException al llamar al constructor de la instancia, asegúrese de colocar el campo Diccionario antes de los campos de "miembro de la enumeración" en su clase. Esto se debe a que los inicializadores de campo estático se llaman en orden de declaración, y antes del constructor estático, creando la situación extraña y necesaria pero confusa de que se puede llamar al constructor de instancia antes de que se hayan inicializado todos los campos estáticos, y antes de que se llame al constructor estático.
llenando este mapeo en el constructor de instancia
instance[name] = this;
y agregar operador de conversión de tipo definido por el usuario
public static explicit operator AuthenticationMethod(string str) { AuthenticationMethod result; if (instance.TryGetValue(str, out result)) return result; else throw new InvalidCastException(); }
Puede hacer referencia al nombre en lugar del valor utilizando ToString ()
Console.WriteLine("Auth method: {0}", AuthenticationMethod.Forms.ToString());
La documentación está aquí:
http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx
... y si nombra sus enumeraciones en Pascal Case (como lo hago yo, como ThisIsMyEnumValue = 1, etc.), entonces podría usar una expresión regular muy sencilla para imprimir el formulario descriptivo:
static string ToFriendlyCase(this string EnumString)
{
return Regex.Replace(EnumString, "(?!^)([A-Z])", " $1");
}
que se puede llamar fácilmente desde cualquier cadena:
Console.WriteLine("ConvertMyCrazyPascalCaseSentenceToFriendlyCase".ToFriendlyCase());
Salidas:
Convertir mi sentencia de Crazy Pascal en un caso amistoso
Eso ahorra el correr por todas las casas creando atributos personalizados y adjuntándolos a sus enumeraciones o utilizando tablas de búsqueda para casar un valor de enumeración con una cadena amigable y, lo mejor de todo, es autoadministrado y se puede usar en cualquier cadena de Pascal Case que es infinitamente Más reutilizable. Por supuesto, no le permite tener un nombre descriptivo diferente al enumeración que proporciona su solución.
Aunque me gusta tu solución original para escenarios más complejos. Podría llevar su solución un paso más allá y convertir su GetStringValue en un método de extensión de su enumeración y luego no tendría que hacer referencia a ella como StringEnum.GetStringValue ...
public static string GetStringValue(this AuthenticationMethod value)
{
string output = null;
Type type = value.GetType();
FieldInfo fi = type.GetField(value.ToString());
StringValue[] attrs = fi.GetCustomAttributes(typeof(StringValue), false) as StringValue[];
if (attrs.Length > 0)
output = attrs[0].Value;
return output;
}
A continuación, puede acceder fácilmente desde su instancia de enumeración:
Console.WriteLine(AuthenticationMethod.SSO.GetStringValue());
Quería publicar esto como un comentario a la publicación citada a continuación, pero no pude porque no tengo suficiente reputación, así que por favor, no voten a la baja. El código contenía un error y quería señalarlo a las personas que intentaban usar esta solución:
[TypeConverter(typeof(CustomEnumTypeConverter(typeof(MyEnum))] public enum MyEnum { // The custom type converter will use the description attribute [Description("A custom description")] ValueWithCustomDescription, // This will be exposed exactly. Exact }
debiera ser
[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
// The custom type converter will use the description attribute
[Description("A custom description")]
ValueWithCustomDescription,
// This will be exposed exactly.
Exact
}
¡Brillante!
Realmente me gusta la respuesta de Jakub Šturc, pero su defecto es que no se puede usar con una declaración de cambio de caso. Aquí hay una versión ligeramente modificada de su respuesta que puede usarse con una declaración de cambio:
public sealed class AuthenticationMethod
{
#region This code never needs to change.
private readonly string _name;
public readonly Values Value;
private AuthenticationMethod(Values value, String name){
this._name = name;
this.Value = value;
}
public override String ToString(){
return _name;
}
#endregion
public enum Values
{
Forms = 1,
Windows = 2,
SSN = 3
}
public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (Values.Forms, "FORMS");
public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (Values.Windows, "WINDOWS");
public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (Values.SSN, "SSN");
}
Entonces, obtienes todos los beneficios de la respuesta de Jakub Šturc, y además podemos usarlo con una declaración de cambio como la siguiente:
var authenticationMethodVariable = AuthenticationMethod.FORMS; // Set the "enum" value we want to use.
var methodName = authenticationMethodVariable.ToString(); // Get the user-friendly "name" of the "enum" value.
// Perform logic based on which "enum" value was chosen.
switch (authenticationMethodVariable.Value)
{
case authenticationMethodVariable.Values.Forms: // Do something
break;
case authenticationMethodVariable.Values.Windows: // Do something
break;
case authenticationMethodVariable.Values.SSN: // Do something
break;
}
Si ha venido aquí para implementar un "Enum" simple pero cuyos valores son cadenas en lugar de ints, aquí está la solución más simple:
public sealed class MetricValueList
{
public static readonly string Brand = "A4082457-D467-E111-98DC-0026B9010912";
public static readonly string Name = "B5B5E167-D467-E111-98DC-0026B9010912";
}
Implementación:
var someStringVariable = MetricValueList.Brand;
Solo usa el método ToString()
public enum any{Tomato=0,Melon,Watermelon}
Para referenciar la cadena de Tomato
, solo usa
any.Tomato.ToString();
Una solución muy simple para esto con .Net 4.0 y superior. No se necesita ningún otro código.
public enum MyStatus
{
Active = 1,
Archived = 2
}
Para obtener la cadena sobre solo usar:
MyStatus.Active.ToString("f");
o
MyStatus.Archived.ToString("f");`
El valor será "Activo" o "Archivado".
Para ver los diferentes formatos de cadena (la "f" de arriba) al llamar a Enum.ToString
consulte esta página de cadenas de formato de enumeración .
Uso el atributo Descripción del espacio de nombres System.ComponentModel. Simplemente decore la enumeración y luego use este código para recuperarlo:
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)
{
//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();
}
Como ejemplo:
public enum Cycle : int
{
[Description("Daily Cycle")]
Daily = 1,
Weekly,
Monthly
}
Este código está muy bien diseñado para enumeraciones en las que no necesita un "Nombre descriptivo" y solo devolverá el .ToString () de la enumeración.
Utilizo una combinación de varias de las sugerencias anteriores, combinadas con algo de almacenamiento en caché. Ahora, obtuve la idea de un código que encontré en algún lugar de la red, pero tampoco recuerdo dónde lo obtuve ni lo encontré. Entonces, si alguien encuentra algo similar, por favor comente con la atribución.
De todos modos, el uso involucra a los convertidores de tipo, por lo que si está vinculado a la interfaz de usuario, ''simplemente funciona''. Puede extenderse con el patrón de Jakub para la búsqueda rápida de códigos al inicializar el convertidor de tipos en los métodos estáticos.
El uso base se vería así
[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
// The custom type converter will use the description attribute
[Description("A custom description")]
ValueWithCustomDescription,
// This will be exposed exactly.
Exact
}
El código para el convertidor de tipo de enumeración personalizado sigue:
public class CustomEnumTypeConverter<T> : EnumConverter
where T : struct
{
private static readonly Dictionary<T,string> s_toString =
new Dictionary<T, string>();
private static readonly Dictionary<string, T> s_toValue =
new Dictionary<string, T>();
private static bool s_isInitialized;
static CustomEnumTypeConverter()
{
System.Diagnostics.Debug.Assert(typeof(T).IsEnum,
"The custom enum class must be used with an enum type.");
}
public CustomEnumTypeConverter() : base(typeof(T))
{
if (!s_isInitialized)
{
Initialize();
s_isInitialized = true;
}
}
protected void Initialize()
{
foreach (T item in Enum.GetValues(typeof(T)))
{
string description = GetDescription(item);
s_toString[item] = description;
s_toValue[description] = item;
}
}
private static string GetDescription(T optionValue)
{
var optionDescription = optionValue.ToString();
var optionInfo = typeof(T).GetField(optionDescription);
if (Attribute.IsDefined(optionInfo, typeof(DescriptionAttribute)))
{
var attribute =
(DescriptionAttribute)Attribute.
GetCustomAttribute(optionInfo, typeof(DescriptionAttribute));
return attribute.Description;
}
return optionDescription;
}
public override object ConvertTo(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture,
object value, Type destinationType)
{
var optionValue = (T)value;
if (destinationType == typeof(string) &&
s_toString.ContainsKey(optionValue))
{
return s_toString[optionValue];
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture, object value)
{
var stringValue = value as string;
if (!string.IsNullOrEmpty(stringValue) && s_toValue.ContainsKey(stringValue))
{
return s_toValue[stringValue];
}
return base.ConvertFrom(context, culture, value);
}
}
}
Yo uso un método de extensión:
public static class AttributesHelperExtension
{
public static string ToDescription(this Enum value)
{
var da = (DescriptionAttribute[])(value.GetType().GetField(value.ToString())).GetCustomAttributes(typeof(DescriptionAttribute), false);
return da.Length > 0 ? da[0].Description : value.ToString();
}
}
Ahora decora la enum
con:
public enum AuthenticationMethod
{
[Description("FORMS")]
FORMS = 1,
[Description("WINDOWSAUTHENTICATION")]
WINDOWSAUTHENTICATION = 2,
[Description("SINGLESIGNON ")]
SINGLESIGNON = 3
}
Cuando usted llama
AuthenticationMethod.FORMS.ToDescription()
obtendrá "FORMS"
.
viejo post pero ...
La respuesta a esto puede ser muy simple. Utilice Enum.ToString() la función
Hay 6 sobrecargas de esta función, puede usar Enum.Tostring ("F") o Enum.ToString () para devolver el valor de la cadena. No hay necesidad de molestarse con nada más. Aquí hay una demostración de trabajo
Tenga en cuenta que esta solución puede no funcionar para todos los compiladores ( esta demostración no funciona como se esperaba ) pero al menos funciona para el compilador más reciente.
Estoy de acuerdo con Keith, pero no puedo votar (todavía).
Utilizo un método estático y una declaración swith para devolver exactamente lo que quiero. En la base de datos almaceno tinyint y mi código solo usa la enumeración real, por lo que las cadenas son para los requisitos de la interfaz de usuario. Después de numerosas pruebas, esto dio como resultado el mejor rendimiento y el mayor control sobre la salida.
public static string ToSimpleString(this enum)
{
switch (enum)
{
case ComplexForms:
return "ComplexForms";
break;
}
}
public static string ToFormattedString(this enum)
{
switch (enum)
{
case ComplexForms:
return "Complex Forms";
break;
}
}
Sin embargo, según algunas cuentas, esto conduce a una posible pesadilla de mantenimiento y algo de olor de código. Intento vigilar los enums que son largos y muchos enums, o los que cambian con frecuencia. De lo contrario, esta ha sido una gran solución para mí.
Cuando estoy en una situación como esa, propongo la solución a continuación.
Y como clase consumidora podrías tener
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyApp.Dictionaries
{
class Greek
{
public static readonly string Alpha = "Alpha";
public static readonly string Beta = "Beta";
public static readonly string Gamma = "Gamma";
public static readonly string Delta = "Delta";
private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();
static Greek() {
Dictionary.Add(1, Alpha);
Dictionary.Add(2, Beta);
Dictionary.Add(3, Gamma);
Dictionary.Add(4, Delta);
}
public static string getById(int id){
return Dictionary.GetByFirst(id);
}
public static int getByValue(string value)
{
return Dictionary.GetBySecond(value);
}
}
}
Y utilizando un diccionario bidireccional: en base a esto ( https://.com/a/255638/986160 ), asumiendo que las claves se asociarán con valores únicos en el diccionario y similares a ( https://.com/a/255630/986160 ) pero un poco más elegante. Este diccionario también es enumerable y puede ir y venir de ints a cadenas. Además, no tienes que tener ninguna cadena en tu base de código con la excepción de esta clase.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace MyApp.Dictionaries
{
class BiDictionary<TFirst, TSecond> : IEnumerable
{
IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();
public void Add(TFirst first, TSecond second)
{
firstToSecond.Add(first, second);
secondToFirst.Add(second, first);
}
public TSecond this[TFirst first]
{
get { return GetByFirst(first); }
}
public TFirst this[TSecond second]
{
get { return GetBySecond(second); }
}
public TSecond GetByFirst(TFirst first)
{
return firstToSecond[first];
}
public TFirst GetBySecond(TSecond second)
{
return secondToFirst[second];
}
public IEnumerator GetEnumerator()
{
return GetFirstEnumerator();
}
public IEnumerator GetFirstEnumerator()
{
return firstToSecond.GetEnumerator();
}
public IEnumerator GetSecondEnumerator()
{
return secondToFirst.GetEnumerator();
}
}
}
Esta es otra forma de realizar la tarea de asociar cadenas con enumeraciones:
struct DATABASE {
public enum enums {NOTCONNECTED, CONNECTED, ERROR}
static List<string> strings =
new List<string>() {"Not Connected", "Connected", "Error"};
public string GetString(DATABASE.enums value) {
return strings[(int)value];
}
}
Este método se llama así:
public FormMain() {
DATABASE dbEnum;
string enumName = dbEnum.GetString(DATABASE.enums.NOTCONNECTED);
}
Puede agrupar enumeraciones relacionadas en su propia estructura. Dado que este método utiliza el tipo de enumeración, puede usar Intellisense para mostrar la lista de enumeraciones al realizar la GetString()
llamada.
Opcionalmente, puede utilizar el nuevo operador en la DATABASE
estructura. No usarlo significa que las cadenas List
no se asignan hasta que se realiza la primera GetString()
llamada.
Mi respuesta, trabajar en la respuesta de @ user29964 (que es por mucho la más simple y cercana a una Enum) es
public class StringValue : System.Attribute
{
private string _value;
public StringValue(string value)
{
_value = value;
}
public string Value
{
get { return _value; }
}
public static string GetStringValue(Enum Flagvalue)
{
Type type = Flagvalue.GetType();
string[] flags = Flagvalue.ToString().Split('','').Select(x => x.Trim()).ToArray();
List<string> values = new List<string>();
for (int i = 0; i < flags.Length; i++)
{
FieldInfo fi = type.GetField(flags[i].ToString());
StringValue[] attrs =
fi.GetCustomAttributes(typeof(StringValue),
false) as StringValue[];
if (attrs.Length > 0)
{
values.Add(attrs[0].Value);
}
}
return String.Join(",", values);
}
uso
[Flags]
public enum CompeteMetric
{
/// <summary>
/// u
/// </summary>
[StringValue("u")]//Json mapping
Basic_UniqueVisitors = 1 //Basic
,
/// <summary>
/// vi
/// </summary>
[StringValue("vi")]//json mapping
Basic_Visits = 2// Basic
,
/// <summary>
/// rank
/// </summary>
[StringValue("rank")]//json mapping
Basic_Rank = 4//Basic
}
Ejemplo
CompeteMetric metrics = CompeteMetric.Basic_Visits | CompeteMetric.Basic_Rank;
string strmetrics = StringValue.GetStringValue(metrics);
esto devolverá "vi, rango"
Muchas respuestas geniales aquí, pero en mi caso no resolvieron lo que quería de una "enumeración de cadena", que era:
- Utilizable en una declaración de cambio, por ejemplo, switch (myEnum)
- Se puede utilizar en parámetros de función, por ejemplo, foo (tipo myEnum)
- Puede ser referenciado, por ejemplo, myEnum.FirstElement
- Puedo usar cadenas, por ejemplo, foo ("FirstElement") == foo (myEnum.FirstElement)
1,2 y 4 pueden resolverse con un C # Typedef de una cadena (ya que las cadenas son conmutables en c #)
3 pueden resolverse mediante constantes estáticas. Entonces, si tiene las mismas necesidades, este es el enfoque más simple:
public sealed class Types
{
private readonly String name;
private Types(String name)
{
this.name = name;
}
public override String ToString()
{
return name;
}
public static implicit operator Types(string str)
{
return new Types(str);
}
public static implicit operator string(Types str)
{
return str.ToString();
}
#region enum
public const string DataType = "Data";
public const string ImageType = "Image";
public const string Folder = "Folder";
#endregion
}
Esto permite por ejemplo:
public TypeArgs(Types SelectedType)
{
Types SelectedType = SelectedType
}
y
public TypeObject CreateType(Types type)
{
switch (type)
{
case Types.ImageType:
//
break;
case Types.DataType:
//
break;
}
}
Donde CreateType se puede llamar con una cadena o un tipo. Sin embargo, el inconveniente es que cualquier cadena es automáticamente una enumeración válida , esto podría modificarse, pero luego requeriría algún tipo de función de inicio ... ¿o posiblemente la conversión explícita interna?
Ahora bien, si un valor int era importante para ti (quizás para la velocidad de comparación), podrías usar algunas ideas de la fantástica respuesta de Jakub Šturc y hacer algo un poco loco, esta es mi prueba:
public sealed class Types
{
private static readonly Dictionary<string, Types> strInstance = new Dictionary<string, Types>();
private static readonly Dictionary<int, Types> intInstance = new Dictionary<int, Types>();
private readonly String name;
private static int layerTypeCount = 0;
private int value;
private Types(String name)
{
this.name = name;
value = layerTypeCount++;
strInstance[name] = this;
intInstance[value] = this;
}
public override String ToString()
{
return name;
}
public static implicit operator Types(int val)
{
Types result;
if (intInstance.TryGetValue(val, out result))
return result;
else
throw new InvalidCastException();
}
public static implicit operator Types(string str)
{
Types result;
if (strInstance.TryGetValue(str, out result))
{
return result;
}
else
{
result = new Types(str);
return result;
}
}
public static implicit operator string(Types str)
{
return str.ToString();
}
public static bool operator ==(Types a, Types b)
{
return a.value == b.value;
}
public static bool operator !=(Types a, Types b)
{
return a.value != b.value;
}
#region enum
public const string DataType = "Data";
public const string ImageType = "Image";
#endregion
}
pero por supuesto "Types bob = 4;" no tendría sentido a menos que primero los hubieras inicializado, lo que de alguna forma anularía el punto ...
Pero en teoría, TypeA == TypeB sería más rápido ...
Opción 1:
public sealed class FormsAuth
{
public override string ToString{return "Forms Authtentication";}
}
public sealed class WindowsAuth
{
public override string ToString{return "Windows Authtentication";}
}
public sealed class SsoAuth
{
public override string ToString{return "SSO";}
}
y entonces
object auth = new SsoAuth(); //or whatever
//...
//...
// blablabla
DoSomethingWithTheAuth(auth.ToString());
Opcion 2:
public enum AuthenticationMethod
{
FORMS = 1,
WINDOWSAUTHENTICATION = 2,
SINGLESIGNON = 3
}
public class MyClass
{
private Dictionary<AuthenticationMethod, String> map = new Dictionary<AuthenticationMethod, String>();
public MyClass()
{
map.Add(AuthenticationMethod.FORMS,"Forms Authentication");
map.Add(AuthenticationMethod.WINDOWSAUTHENTICATION ,"Windows Authentication");
map.Add(AuthenticationMethod.SINGLESIGNON ,"SSo Authentication");
}
}
Para mí, el enfoque pragmático es clase dentro de clase, muestra:
public class MSEModel
{
class WITS
{
public const string DATE = "5005";
public const string TIME = "5006";
public const string MD = "5008";
public const string ROP = "5075";
public const string WOB = "5073";
public const string RPM = "7001";
...
}
Bueno, después de leer todo lo anterior, siento que los chicos han complicado el problema de transformar los enumeradores en cadenas. Me gustó la idea de tener atributos sobre campos enumerados, pero creo que los atributos se utilizan principalmente para metadatos, pero en su caso, creo que todo lo que necesita es algún tipo de localización.
public enum Color
{ Red = 1, Green = 2, Blue = 3}
public static EnumUtils
{
public static string GetEnumResourceString(object enumValue)
{
Type enumType = enumValue.GetType();
string value = Enum.GetName(enumValue.GetType(), enumValue);
string resourceKey = String.Format("{0}_{1}", enumType.Name, value);
string result = Resources.Enums.ResourceManager.GetString(resourceKey);
if (string.IsNullOrEmpty(result))
{
result = String.Format("{0}", value);
}
return result;
}
}
Ahora, si intentamos llamar al método anterior, podemos llamarlo de esta manera
public void Foo()
{
var col = Color.Red;
Console.WriteLine (EnumUtils.GetEnumResourceString (col));
}
Todo lo que necesita hacer es crear un archivo de recursos que contenga todos los valores del enumerador y las cadenas correspondientes
Resource Name Resource Value Color_Red My String Color in Red Color_Blue Blueeey Color_Green Hulk Color
Lo que realmente es bueno es que será muy útil si necesita que su aplicación se localice, ya que todo lo que necesita hacer es crear otro archivo de recursos con su nuevo idioma. y Voe-la!
Si lo estoy entendiendo correctamente, simplemente puede usar .ToString () para recuperar el nombre de la enumeración del valor (Suponiendo que ya se haya emitido como la Enumeración); Si tuviste el int desnudo (digamos desde una base de datos o algo así), primero puedes convertirlo en la enumeración. Ambos métodos a continuación le darán el nombre enum.
AuthenticationMethod myCurrentSetting = AuthenticationMethod.FORMS;
Console.WriteLine(myCurrentSetting); // Prints: FORMS
string name = Enum.GetNames(typeof(AuthenticationMethod))[(int)myCurrentSetting-1];
Console.WriteLine(name); // Prints: FORMS
Sin embargo, tenga en cuenta que la segunda técnica asume que está utilizando ints y que su índice se basa en 1 (no en 0). La función GetNames también es bastante pesada en comparación, estás generando una matriz completa cada vez que se llama. Como puede ver en la primera técnica, .ToString () en realidad se llama implícitamente. Ambos ya están mencionados en las respuestas, por supuesto, solo estoy tratando de aclarar las diferencias entre ellos.
Si piensa en el problema que estamos tratando de resolver, no es una enumeración que necesitamos en absoluto. Necesitamos un objeto que permita asociar un cierto número de valores entre sí; en otras palabras, para definir una clase.
El patrón de enumeración de tipo seguro de Jakub Šturc es la mejor opción que veo aquí.
Míralo:
- Tiene un constructor privado por lo que solo la clase en sí puede definir los valores permitidos.
- Es una clase sellada, por lo que los valores no pueden modificarse a través de la herencia.
- Es de tipo seguro, permitiendo que sus métodos requieran solo ese tipo.
- No hay un impacto en el rendimiento de la reflexión incurrido al acceder a los valores.
- Y, por último, se puede modificar para asociar más de dos campos juntos, por ejemplo, un Nombre, una Descripción y un Valor numérico.
Use el objeto Enum.Parse (System.Type enumType, valor de cadena, bool ignoreCase); Lo obtuve de http://blogs.msdn.com/b/tims/archive/2004/04/02/106310.aspx
basado en MSDN: http://msdn.microsoft.com/en-us/library/cc138362.aspx
foreach (string str in Enum.GetNames(typeof(enumHeaderField)))
{
Debug.WriteLine(str);
}
str serán los nombres de los campos