Эффективность может зависеть от включенных чисел. Вы тестируете КРОШЕЧНУЮ часть доступного пространства задач и всех загруженных с передней стороны. Вы проверяете первый 1 миллион * 10 = 1 миллиард непрерывных входных комбинаций, но фактическое пространство задач является приблизительно 4,2 миллиардами, в квадрате, или 1.8e19 комбинации.
выполнение общих математических операций библиотеки как это должно быть амортизировано по целому пространству задач. Мне было бы интересно видеть результаты более нормализованного входного распределения.
Спасибо всем за все ответы. Наконец, я использовал комбинацию из Rex M и adrianbanks и добавил свои собственные улучшения, чтобы упростить привязку к ComboBox.
Изменения были необходимы, потому что, работая над кодом, я понял, что иногда мне нужно иметь возможность исключить один элемент перечисления из комбо. Например,
Enum Complexity
{
// this will be used in filters,
// but not in module where I have to assign Complexity to a field
AllComplexities,
NotSoComplex,
LittleComplex,
Complex,
VeryComplex
}
Так что иногда я хочу, чтобы в списке выбора отображались все, кроме AllComplexities (в модулях добавления и редактирования), а в другой раз - все (в фильтрах).
Вот что я сделал:
Полный код показан ниже
public static string GetDescription(this System.Enum value)
{
string enumID = string.Empty;
string enumDesc = string.Empty;
try
{
// try to lookup Description attribute
FieldInfo field = value.GetType().GetField(value.ToString());
object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true);
if (attribs.Length > 0)
{
enumID = ((DescriptionAttribute)attribs[0]).Description;
enumDesc = TranslationHelper.GetTranslation(enumID);
}
if (string.IsNullOrEmpty(enumID) || TranslationHelper.IsTranslationMissing(enumDesc))
{
// try to lookup translation from EnumName_EnumValue
string[] enumName = value.GetType().ToString().Split('.');
enumID = string.Format("{0}_{1}", enumName[enumName.Length - 1], value.ToString());
enumDesc = TranslationHelper.GetTranslation(enumID);
if (TranslationHelper.IsTranslationMissing(enumDesc))
enumDesc = string.Empty;
}
// try to format CamelCase to proper names
if (string.IsNullOrEmpty(enumDesc))
{
Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled);
enumDesc = capitalLetterMatch.Replace(value.ToString(), " $&");
}
}
catch (Exception)
{
// if any error, fallback to string value
enumDesc = value.ToString();
}
return enumDesc;
}
Я создал общий вспомогательный класс на основе Enum, который позволяет легко привязать перечисление к DataSource
public class LocalizableEnum
{
/// <summary>
/// Column names exposed by LocalizableEnum
/// </summary>
public class ColumnNames
{
public const string ID = "EnumValue";
public const string EntityValue = "EnumDescription";
}
}
public class LocalizableEnum<T>
{
private T m_ItemVal;
private string m_ItemDesc;
public LocalizableEnum(T id)
{
System.Enum idEnum = id as System.Enum;
if (idEnum == null)
throw new ArgumentException(string.Format("Type {0} is not enum", id.ToString()));
else
{
m_ItemVal = id;
m_ItemDesc = idEnum.GetDescription();
}
}
public override string ToString()
{
return m_ItemDesc;
}
public T EnumValue
{
get { return m_ID; }
}
public string EnumDescription
{
get { return ToString(); }
}
}
Затем Я создал общий статический метод, который возвращает List>, как показано ниже
public static List<LocalizableEnum<T>> GetEnumList<T>(object excludeMember)
{
List<LocalizableEnum<T>> list =null;
Array listVal = System.Enum.GetValues(typeof(T));
if (listVal.Length>0)
{
string excludedValStr = string.Empty;
if (excludeMember != null)
excludedValStr = ((T)excludeMember).ToString();
list = new List<LocalizableEnum<T>>();
for (int i = 0; i < listVal.Length; i++)
{
T currentVal = (T)listVal.GetValue(i);
if (excludedValStr != currentVal.ToString())
{
System.Enum enumVal = currentVal as System.Enum;
LocalizableEnum<T> enumMember = new LocalizableEnum<T>(currentVal);
list.Add(enumMember);
}
}
}
return list;
}
, и оболочку для возврата списка со всеми членами
public static List<LocalizableEnum<T>> GetEnumList<T>()
{
return GetEnumList<T>(null);
}
Теперь давайте соберем все вместе и свяжемся с фактическим комбо:
// in module where we want to show items with all complexities
// or just filter on one complexity
comboComplexity.DisplayMember = LocalizableEnum.ColumnNames.EnumValue;
comboComplexity.ValueMember = LocalizableEnum.ColumnNames.EnumDescription;
comboComplexity.DataSource = EnumHelper.GetEnumList<Complexity>();
comboComplexity.SelectedValue = Complexity.AllComplexities;
// ....
// and here in edit module where we don't want to see "All Complexities"
comboComplexity.DisplayMember = LocalizableEnum.ColumnNames.EnumValue;
comboComplexity.ValueMember = LocalizableEnum.ColumnNames.EnumDescription;
comboComplexity.DataSource = EnumHelper.GetEnumList<Complexity>(Complexity.AllComplexities);
comboComplexity.SelectedValue = Complexity.VeryComplex; // set default value
Чтобы прочитать выбранное значение и используйте его, я использую следующий код
Complexity selComplexity = (Complexity)comboComplexity.SelectedValue;
Основные дружественные имена
Используйте атрибут описания : *
enum MyEnum
{
[Description("This is black")]
Black,
[Description("This is white")]
White
}
И удобный метод расширения для перечислений:
public static string GetDescription(this Enum value)
{
FieldInfo field = value.GetType().GetField(value.ToString());
object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true);
if(attribs.Length > 0)
{
return ((DescriptionAttribute)attribs[0]).Description;
}
return string.Empty;
}
Используется так:
MyEnum val = MyEnum.Black;
Console.WriteLine(val.GetDescription()); //writes "This is black"
(Обратите внимание, что это не совсем работает для битовых флагов ...)
Для локализации
Существует хорошо зарекомендовавший себя шаблон в .NET для обработки нескольких языков для каждого строкового значения - используйте файл ресурсов и расширьте метод расширения для чтения из файла ресурсов:
public static string GetDescription(this Enum value)
{
FieldInfo field = value.GetType().GetField(value.ToString());
object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true));
if(attribs.Length > 0)
{
string message = ((DescriptionAttribute)attribs[0]).Description;
return resourceMgr.GetString(message, CultureInfo.CurrentCulture);
}
return string.Empty;
}
В любой момент мы можем использовать существующие функции BCL для достичь того, чего мы хотим, это определенно первый путь, который нужно исследовать. Это минимизирует сложность и использует шаблоны, уже знакомые многим другим разработчикам.
Собираем все вместе
Чтобы заставить это связываться с DropDownList, мы, вероятно, хотим отслеживать реальные значения перечисления в нашем элементе управления и ограничить переведенное понятное имя визуальным сахаром. Мы можем сделать это, используя анонимный тип и свойства DataField в списке:
<asp:DropDownList ID="myDDL"
DataTextField="Description"
DataValueField="Value" />
myDDL.DataSource = Enum.GetValues(typeof(MyEnum)).OfType<MyEnum>().Select(
val => new { Description = val.GetDescription(), Value = val.ToString() });
myDDL.DataBind();
Давайте разберем эту строку DataSource:
Enum.GetValues (typeof (MyEnum))
, что дает нам слабо типизированный массив
значений OfType ()
, который преобразует массив в IEnumerable
Select ()
и предоставляем лямбду, которая проецирует новый объект с двумя полями, Description и Value. Свойства DataTextField и DataValueField оцениваются рефлексивно во время привязки данных, поэтому они будут искать поля в DataItem с совпадающими именами.
- Примечание в основной статье,
Использование атрибутов, как в других ответах, - хороший способ, но если вы просто хотите использовать текст из значений перечисления, следующий код будет разделен на основе верблюжий регистр значения:
public static string GetDescriptionOf(Enum enumType)
{
Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled);
return capitalLetterMatch.Replace(enumType.ToString(), " $&");
}
Вызов GetDescriptionOf (Complexity.NotSoComplex)
вернет Not So Complex
. Это можно использовать с любым значением перечисления .
Чтобы сделать его более полезным, вы можете сделать его методом расширения:
public static string ToFriendlyString(this Enum enumType)
{
Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled);
return capitalLetterMatch.Replace(enumType.ToString(), " $&");
}
Теперь вы вызываете его, используя Complexity.NotSoComplex.ToFriendlyString ( )
, чтобы вернуть Not So Complex
.
EDIT : только что заметил, что в вашем вопросе вы упоминаете, что вам нужно локализовать текст. В этом случае я бы использовал атрибут, содержащий ключ для поиска локализованного значения, но по умолчанию используется метод дружественной строки в качестве последнего средства, если локализованный текст не может быть найден. Вы должны определить свои перечисления следующим образом:
enum Complexity
{
[LocalisedEnum("Complexity.NotSoComplex")]
NotSoComplex,
[LocalisedEnum("Complexity.LittleComplex")]
LittleComplex,
[LocalisedEnum("Complexity.Complex")]
Complex,
[LocalisedEnum("Complexity.VeryComplex")]
VeryComplex
}
Вам также понадобится этот код:
[AttributeUsage(AttributeTargets.Field, AllowMultiple=false, Inherited=true)]
public class LocalisedEnum : Attribute
{
public string LocalisationKey{get;set;}
public LocalisedEnum(string localisationKey)
{
LocalisationKey = localisationKey;
}
}
public static class LocalisedEnumExtensions
{
public static string ToLocalisedString(this Enum enumType)
{
// default value is the ToString();
string description = enumType.ToString();
try
{
bool done = false;
MemberInfo[] memberInfo = enumType.GetType().GetMember(enumType.ToString());
if (memberInfo != null && memberInfo.Length > 0)
{
object[] attributes = memberInfo[0].GetCustomAttributes(typeof(LocalisedEnum), false);
if (attributes != null && attributes.Length > 0)
{
LocalisedEnum descriptionAttribute = attributes[0] as LocalisedEnum;
if (description != null && descriptionAttribute != null)
{
string desc = TranslationHelper.GetTranslation(descriptionAttribute.LocalisationKey);
if (desc != null)
{
description = desc;
done = true;
}
}
}
}
if (!done)
{
Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled);
description = capitalLetterMatch.Replace(enumType.ToString(), " $&");
}
}
catch
{
description = enumType.ToString();
}
return description;
}
}
Чтобы получить локализованные описания, вы должны затем вызвать:
Complexity.NotSoComplex.ToLocalisedString()
Это имеет несколько резервных случаев:
Определен атрибут LocalisedEnum
, он будет использовать ключ для поиска переведенного текста LocalisedEnum
, но локализованный текст не найден, по умолчанию используется верблюд -case split метод LocalisedEnum
, он будет использовать метод разделения верблюжьего регистра Я использую следующий класс
public class EnumUtils
{
/// <summary>
/// Reads and returns the value of the Description Attribute of an enumeration value.
/// </summary>
/// <param name="value">The enumeration value whose Description attribute you wish to have returned.</param>
/// <returns>The string value portion of the Description attribute.</returns>
public static string StringValueOf(Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
{
return attributes[0].Description;
}
else
{
return value.ToString();
}
}
/// <summary>
/// Returns the Enumeration value that has a given Description attribute.
/// </summary>
/// <param name="value">The Description attribute value.</param>
/// <param name="enumType">The type of enumeration in which to search.</param>
/// <returns>The enumeration value that matches the Description value provided.</returns>
/// <exception cref="ArgumentException">Thrown when the specified Description value is not found with in the provided Enumeration Type.</exception>
public static object EnumValueOf(string value, Type enumType)
{
string[] names = Enum.GetNames(enumType);
foreach (string name in names)
{
if (StringValueOf((Enum)Enum.Parse(enumType, name)).Equals(value))
{
return Enum.Parse(enumType, name);
}
}
throw new ArgumentException("The string is not a description or value of the specified enum.");
}
, который считывает атрибут с именем description
public enum PuppyType
{
[Description("Cute Puppy")]
CutePuppy = 0,
[Description("Silly Puppy")]
SillyPuppy
}