среда, 10 октября 2007 г.

Enum'ы с человеческим лицом!

Сегодня речь я поведу о том, как облегчить себе жизнь при работе с enum-типами. То, что использовать их нужно понимают, я думаю, все. Это удобно: код становится понятнее, вероятность совершить ошибку (если использовать вместо enum «магические» числа) – ниже и т.д. и т.п.
Все это, конечно, хорошо… Но что делать, если необходимо отображать пользователю списки (например, combobox'ы) состоящие из этих самых enum (или сводящиеся к ним)? Естественно, что пользователь не знает английского, да и в любом случае – адекватно воспринимать замену пробела символом подчеркивания он точно не будет :)
Если вспомнить про то, что юникод для дотнета – это «родное», а студия еще и корректно работает с русскими именами функций и переменных, то можно, конечно, называть значения в нашем enum русскими именами. Но! Это ж умучаешься раскладки переключать :)
Поэтому самое простое неправильное решение – это, как правило, использование строкового массива, в котором задаются отображаемые пользователю русские имена.
Можно пойти дальше и использовать вместо массива, например, Dictionary. Этот способ как минимум более надежен и шанс , что в программе появится странная ошибка если мы решим добавить еще несколько элементов в enum, резко уменьшится.
Кстати, этот способ имеет право на жизнь. Однако – он неудобен. Намного удобнее, когда описания элементов enum хранятся вместе с ними. Даже очень удобно :)
Как этого добиться? (И вот тут все должны громко воскликнуть – «Ну конечно атрибуты!»)
Первое что нам понадобится – это атрибут, через который мы будем задавать описание:

[AttributeUsage(AttributeTargets.Field)]
public class EnumDescriptionAttribute: Attribute
{
    public readonly string description;

    public EnumDescriptionAttribute(string description)
    {
        this.description = description;
    }
}

public enum etest
{
    [EnumNameAttribute("Значение 1")]
    value1,
    [EnumNameAttribute("Значение 2")]
    value2,
    …
}
Как видите – здесь все просто :)
А теперь самое сложное: получение этих описаний (чтобы отдать их пользователю).

Примечание: и вот тут-то руки и опускаются – сразу после того, как мы слышим жуткое слово reflection :) Но не пугайтесь, все не так страшно ;)

Чтобы получить наш атрибут-описание для известного значения, надо вызвать следующий код:


((EnumDescriptionAttribute)typeof(etest).GetField(p.ToString()).GetCustomAttributes(typeof(EnumDescriptionAttribute), true)[0]).description;


Примечание: стра-а-ашно??? :)

Чтобы получить имена для всех значений enum (например, чтобы сформировать список, из которого будет производиться выбор нужного значения):

public static string[] GetAllEnumDescription()
{
    T[] vals=(T[])Enum.GetValues(typeof(T));
    string[] names = new string[vals.Length];
    for (int i = 0; i < vals.Length; i++)
        names[i] = GetEnumDescription(vals[i]);
    return names;
}

public static string GetEnumDescription(T p)
{
    return ((EnumDescriptionAttribute)typeof(T).GetField(p.ToString()).GetCustomAttributes(typeof(EnumDescriptionAttribute), true)[0]).description;
}


Примечание: я решил, что данный вариант (использующий generics) функции будет полезнее – этот вариант функции можно использовать для любого enum (при условии, что у его элементов есть наш атрибут). Кому не нравится – его легко можно переделать.

Вообщем-то и всё…

P.S.: ой, нет! Чуть не забыл: если в функции GetAllEnumDescription использовать конструкцию yeld return – то она будет еще более удобна :) и один new можно будет убрать…

Комментариев нет:

Отправить комментарий