Второй является классическим примером Антишаблон Стрелки , Таким образом, я избежал бы его...
, Если Ваши условия являются слишком длинными, извлекают их в методы/свойства.
Имена значений перечисления должны соответствовать тем же правилам именования, что и все идентификаторы в C #, поэтому только имя является правильным.
Если у вас есть следующее перечисление:
public enum MyEnum {
First,
Second,
Third
}
Вы можете объявить методы расширения для MyEnum
(как и для любого другого типа). Я только что придумал:
namespace Extension {
public static class ExtensionMethods {
public static string EnumValue(this MyEnum e) {
switch (e) {
case MyEnum.First:
return "First Friendly Value";
case MyEnum.Second:
return "Second Friendly Value";
case MyEnum.Third:
return "Third Friendly Value";
}
return "Horrible Failure!!";
}
}
}
С помощью этого метода расширения теперь допустимо следующее:
Console.WriteLine(MyEnum.First.EnumValue());
Надеюсь, это поможет !!
Одна из проблем этого трюка заключается в том, что атрибут описания не может быть локализован. Мне нравится техника Саши Барбера, в которой он создает свою собственную версию атрибута Description, которая будет получать значения из соответствующего диспетчера ресурсов.
http://www.codeproject.com/KB/WPF/FriendlyEnums.aspx
] Хотя статья посвящена проблеме, с которой обычно сталкиваются разработчики WPF при привязке к перечислениям, вы можете сразу перейти к той части, где он создает LocalizableDescriptionAttribute.
Имена перечислений живут по тем же правилам, что и обычные имена переменных, то есть без пробелов или точек в середине имен ... Я все еще считаю первое довольно дружелюбным ...
Они следуют тем же правилам именования, что и имена переменных. Поэтому они не должны содержать пробелов.
Также то, что вы предлагаете, в любом случае было бы очень плохой практикой.
Вы можете использовать атрибут Описание
, как предложил Юрий. Следующий метод расширения упрощает получение описания для заданного значения перечисления:
public static string GetDescription(this Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name != null)
{
FieldInfo field = type.GetField(name);
if (field != null)
{
DescriptionAttribute attr =
Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
{
return attr.Description;
}
}
}
return null;
}
Вы можете использовать его так:
public enum MyEnum
{
[Description("Description for Foo")]
Foo,
[Description("Description for Bar")]
Bar
}
MyEnum x = MyEnum.Foo;
string description = x.GetDescription();
Нет, но вы можете использовать DescriptionAttribute , чтобы выполнить то, что вы ищете.
Вы можете использовать атрибут Описание
, чтобы получить это понятное имя. Вы можете использовать приведенный ниже код:
public static string ToStringEnums(Enum en)
{
Type type = en.GetType();
MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
return ((DescriptionAttribute)attrs[0]).Description;
}
return en.ToString();
}
Пример того, когда вы хотели бы использовать этот метод: когда ваше значение перечисления EncryptionProviderType
и вы хотите, чтобы enumVar.Tostring ()
возвращал «Тип поставщика шифрования».
Предварительное условие: все члены перечисления должны применяться с атрибутом [Описание («Строка, возвращаемая Tostring ()»)]
.
Пример перечисления:
enum ExampleEnum
{
[Description("One is one")]
ValueOne = 1,
[Description("Two is two")]
ValueTow = 2
}
И в вашем классе, вы бы использовали его так:
ExampleEnum enumVar = ExampleEnum.ValueOne;
Console.WriteLine(ToStringEnums(enumVar));
public enum myEnum
{
ThisNameWorks,
This_Name_can_be_used_instead,
}
Я полагаю, что вы хотите показать свои значения перечисления пользователю, поэтому вы хотите, чтобы у них было какое-то понятное имя.
Вот мой предложение:
Используйте шаблон типа enum. Хотя для реализации требуются некоторые усилия, это действительно того стоит.
public class MyEnum
{
public static readonly MyEnum Enum1=new MyEnum("This will work",1);
public static readonly MyEnum Enum2=new MyEnum("This.will.work.either",2);
public static readonly MyEnum[] All=new []{Enum1,Enum2};
private MyEnum(string name,int value)
{
Name=name;
Value=value;
}
public string Name{get;set;}
public int Value{get;set;}
public override string ToString()
{
return Name;
}
}
Некоторые замечательные решения уже были опубликованы. Когда я столкнулся с этой проблемой, я хотел пойти обоими путями: преобразовать перечисление в описание и преобразовать строку, соответствующую описанию, в перечисление.
У меня есть два варианта, медленный и быстрый. Оба конвертируют из перечисления в строку и из строки в перечисление. Моя проблема в том, что у меня есть перечисления, подобные этому, где некоторым элементам нужны атрибуты, а некоторым нет. Я не хочу накладывать атрибуты на элементы, которым они не нужны. Всего у меня их около сотни:
public enum POS
{
CC, // Coordinating conjunction
CD, // Cardinal Number
DT, // Determiner
EX, // Existential there
FW, // Foreign Word
IN, // Preposision or subordinating conjunction
JJ, // Adjective
[System.ComponentModel.Description("WP$")]
WPDollar, //$ Possessive wh-pronoun
WRB, // Wh-adverb
[System.ComponentModel.Description("#")]
Hash,
[System.ComponentModel.Description("$")]
Dollar,
[System.ComponentModel.Description("''")]
DoubleTick,
[System.ComponentModel.Description("(")]
LeftParenth,
[System.ComponentModel.Description(")")]
RightParenth,
[System.ComponentModel.Description(",")]
Comma,
[System.ComponentModel.Description(".")]
Period,
[System.ComponentModel.Description(":")]
Colon,
[System.ComponentModel.Description("``")]
DoubleBackTick,
};
Первый метод решения этой проблемы медленный и основан на предложениях, которые я видел здесь и в сети. Он медленный, потому что мы отражаем каждое преобразование:
using System;
using System.Collections.Generic;
namespace CustomExtensions
{
/// <summary>
/// uses extension methods to convert enums with hypens in their names to underscore and other variants
public static class EnumExtensions
{
/// <summary>
/// Gets the description string, if available. Otherwise returns the name of the enum field
/// LthWrapper.POS.Dollar.GetString() yields "$", an impossible control character for enums
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string GetStringSlow(this Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name != null)
{
System.Reflection.FieldInfo field = type.GetField(name);
if (field != null)
{
System.ComponentModel.DescriptionAttribute attr =
Attribute.GetCustomAttribute(field,
typeof(System.ComponentModel.DescriptionAttribute)) as System.ComponentModel.DescriptionAttribute;
if (attr != null)
{
//return the description if we have it
name = attr.Description;
}
}
}
return name;
}
/// <summary>
/// Converts a string to an enum field using the string first; if that fails, tries to find a description
/// attribute that matches.
/// "$".ToEnum<LthWrapper.POS>() yields POS.Dollar
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static T ToEnumSlow<T>(this string value) //, T defaultValue)
{
T theEnum = default(T);
Type enumType = typeof(T);
//check and see if the value is a non attribute value
try
{
theEnum = (T)Enum.Parse(enumType, value);
}
catch (System.ArgumentException e)
{
bool found = false;
foreach (T enumValue in Enum.GetValues(enumType))
{
System.Reflection.FieldInfo field = enumType.GetField(enumValue.ToString());
System.ComponentModel.DescriptionAttribute attr =
Attribute.GetCustomAttribute(field,
typeof(System.ComponentModel.DescriptionAttribute)) as System.ComponentModel.DescriptionAttribute;
if (attr != null && attr.Description.Equals(value))
{
theEnum = enumValue;
found = true;
break;
}
}
if( !found )
throw new ArgumentException("Cannot convert " + value + " to " + enumType.ToString());
}
return theEnum;
}
}
}
Проблема с этим в том, что вы делаете отражение каждый раз. Я не измерял падение производительности от этого, но оно кажется тревожным. Хуже того, мы вычисляем эти дорогостоящие преобразования многократно, не кэшируя их.
Вместо этого мы можем использовать статический конструктор для заполнения некоторых словарей информацией о преобразованиях, а затем просто искать эту информацию, когда это необходимо. Очевидно, статические классы (необходимые для методов расширения) могут иметь конструкторы и поля :)
using System;
using System.Collections.Generic;
namespace CustomExtensions
{
/// <summary>
/// uses extension methods to convert enums with hypens in their names to underscore and other variants
/// I'm not sure this is a good idea. While it makes that section of the code much much nicer to maintain, it
/// also incurs a performance hit via reflection. To circumvent this, I've added a dictionary so all the lookup can be done once at
/// load time. It requires that all enums involved in this extension are in this assembly.
/// </summary>
public static class EnumExtensions
{
//To avoid collisions, every Enum type has its own hash table
private static readonly Dictionary<Type, Dictionary<object,string>> enumToStringDictionary = new Dictionary<Type,Dictionary<object,string>>();
private static readonly Dictionary<Type, Dictionary<string, object>> stringToEnumDictionary = new Dictionary<Type, Dictionary<string, object>>();
static EnumExtensions()
{
//let's collect the enums we care about
List<Type> enumTypeList = new List<Type>();
//probe this assembly for all enums
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
Type[] exportedTypes = assembly.GetExportedTypes();
foreach (Type type in exportedTypes)
{
if (type.IsEnum)
enumTypeList.Add(type);
}
//for each enum in our list, populate the appropriate dictionaries
foreach (Type type in enumTypeList)
{
//add dictionaries for this type
EnumExtensions.enumToStringDictionary.Add(type, new Dictionary<object,string>() );
EnumExtensions.stringToEnumDictionary.Add(type, new Dictionary<string,object>() );
Array values = Enum.GetValues(type);
//its ok to manipulate 'value' as object, since when we convert we're given the type to cast to
foreach (object value in values)
{
System.Reflection.FieldInfo fieldInfo = type.GetField(value.ToString());
//check for an attribute
System.ComponentModel.DescriptionAttribute attribute =
Attribute.GetCustomAttribute(fieldInfo,
typeof(System.ComponentModel.DescriptionAttribute)) as System.ComponentModel.DescriptionAttribute;
//populate our dictionaries
if (attribute != null)
{
EnumExtensions.enumToStringDictionary[type].Add(value, attribute.Description);
EnumExtensions.stringToEnumDictionary[type].Add(attribute.Description, value);
}
else
{
EnumExtensions.enumToStringDictionary[type].Add(value, value.ToString());
EnumExtensions.stringToEnumDictionary[type].Add(value.ToString(), value);
}
}
}
}
public static string GetString(this Enum value)
{
Type type = value.GetType();
string aString = EnumExtensions.enumToStringDictionary[type][value];
return aString;
}
public static T ToEnum<T>(this string value)
{
Type type = typeof(T);
T theEnum = (T)EnumExtensions.stringToEnumDictionary[type][value];
return theEnum;
}
}
}
Посмотрите, насколько плотно теперь работают методы преобразования. Единственный недостаток, о котором я могу думать, это то, что это требует, чтобы все преобразованные перечисления находились в текущей сборке. Кроме того, я беспокоюсь только об экспортируемых перечислениях, но вы можете изменить это, если захотите.
Вот как вызывать методы
string x = LthWrapper.POS.Dollar.GetString();
LthWrapper.POS y = "PRP$".ToEnum<LthWrapper.POS>();