C# Globals/Constants, привязываемые к DDL, но с красивыми именами

По обычным причинам мне нужно использовать некоторые константы в моем приложении.

Подходы, которые я рассматривал:

1) Объявите перечисление:

public enum myOptions
{
  MyOption1 = 72,
  MyOption2 = 31,
  MyOption3 = 44
}

Хотя это хорошо для программирования, и я могу связать перечисление непосредственно с DDL, но «имена» Enum уродливы, когда пользователь их видит. Пользователь увидит «MyOption1», и я хочу, чтобы они увидели «My Option #». 1".

2) Используйте список:

public static List<KeyValuePair<int, string>> myOptions = new List<KeyValuePair<int, string>>
{
 new KeyValuePair<int, string>(77, "My Option #1"),
 new KeyValuePair<int, string>(31, "My Option #2"),
 new KeyValuePair<int, string>(44, "My Option #3")
}

Так что, хотя это очень хорошо связывается с DDL, дает мне хорошее отображаемое значение, а также целочисленное возвращаемое значение, мне не с чем проверять возвращаемое значение. Так, например:

if (selectedOption=????) //I'd have to hardcode the Key or Value I want to test for.

3) Я мог бы собрать хорошую сборку Global/Constants:

static myOptions
{
 public static KeyValuePair<int, string> MyOption1 = new new KeyValuePair<int, string>(77, "My Option #1");
 public static KeyValuePair<int, string> MyOption2 = new new KeyValuePair<int, string>(31, "My Option #2");
 public static KeyValuePair<int, string> MyOption3 = new new KeyValuePair<int, string>(44, "My Option #3");
}

Это дает мне хорошие отображаемые имена, с которыми приятно кодировать, но, насколько я могу судить, у меня нет возможности легко связать это с DDL (мне пришлось бы вручную кодировать это).

Есть ли у кого-нибудь элегантный способ создания констант, которые легко связываются с DDL, где я могу иметь красивое отображаемое имя?

Прямо сейчас единственное, о чем я могу думать, это создание ОБА Enum и List, что кажется раздражающим.


person SteveSteve    schedule 20.07.2011    source источник


Ответы (3)


Я всегда склоняюсь к оформленным значениям перечисления:

public enum myOptions
{
    [Description("My Option #1")]
    MyOption1 = 72,
    [Description("My Option #2")]
    MyOption2 = 31,
    [Description("My Option #3")]
    MyOption3 = 44
}

или еще лучше, вы можете создать собственный атрибут, который будет привязан к файлу ресурсов или настройке конфигурации, чтобы эти данные можно было изменить без перекомпиляции.

public enum myOptions
{
    [Custom("MyOption1Key")]
    MyOption1 = 72,
    [Custom("MyOption2Key")]
    MyOption2 = 31,
    [Custom("MyOption3Key")]
    MyOption3 = 44
}

Обновление для общего извлечения атрибутов из перечисления

public static T GetAttribute<T>(this Enum e) where T : Attribute
{
    FieldInfo fieldInfo = e.GetType().GetField(e.ToString());
    T[] attribs = fieldInfo.GetCustomAttributes(typeof(T), false) as T[];
    return attribs.Length > 0 ? attribs[0] : null;
}
person hunter    schedule 20.07.2011
comment
Как вы предлагаете привязать оформленное перечисление к DDL? - person SteveSteve; 20.07.2011
comment
извлечь атрибут из перечисления - person hunter; 20.07.2011
comment
У меня сложилось впечатление, что мне придется перебирать перечисление, вытягивая каждое описание. Есть ли способ связать его напрямую? Я надеюсь, что есть что-то похожее на то, что я могу сделать со списком пар ключ-значение, где я просто установил ddl.DataSource = new BindingSource(myKVPList, null); - person SteveSteve; 20.07.2011
comment
Извините - только что заметил обновление с предложенным вами методом извлечения. Спасибо за помощь! - person SteveSteve; 20.07.2011
comment
Будьте осторожны с Reflection... при неправильном использовании он может работать очень медленно. - person qJake; 22.07.2011

Основываясь на ответе, предоставленном @hunter, я решил опубликовать свою полную реализацию, так как мне потребовалось некоторое время, чтобы сделать это правильно (все еще учусь здесь...)

// This is the class I used to hold the extensions.
public static class EnumFunctions
{
    // Custom GetAttribute Extension - used to pull an attribute out of the enumerations.
    // This code is as per Hunter's answer, except I added the null check.
    public static T GetAttribute<T>(this Enum e) where T : Attribute
    {
        FieldInfo fieldInfo = e.GetType().GetField(e.ToString());

        // If the enumeration is set to an illegal value (for example 0,
        // when you don't have a 0 in your enum) then the field comes back null.
        // test and avoid the exception.
        if (fieldInfo != null)
        {
            T[] attribs = fieldInfo.GetCustomAttributes(typeof(T), false) as T[];
            return attribs.Length > 0 ? attribs[0] : null;
        }
        else
        {
            return null;
        }
    }

    // Custom GetKeyValuePairs - returns a List of <int,string> key value pairs with
    // each Enum value along with it's Description attribute.
    // This will only work with a decorated Enum. I've not included or tested what
    // happens if your enum doesn't have Description attributes.
    public static List<KeyValuePair<int, string>> GetKeyValuePairs(this Enum e)
    {
        List<KeyValuePair<int, string>> ret = new List<KeyValuePair<int, string>>();

        foreach (Enum val in Enum.GetValues(e.GetType()))
        {
            ret.Add(new KeyValuePair<int, string>(Convert.ToInt32(val), val.GetAttribute<DescriptionAttribute>().Description));
        }
        return ret;
    }

}

В другом месте, чтобы выполнить привязку к DDL, вы можете просто сделать это:

{
    // We need an instance of the enum to call our extension method.
    myOptions o = myOptions.MyOption1;

    // Clear the combobox
    comboBox1.DataSource = null;
    comboBox1.Items.Clear();

    // Bind the combobox
    comboBox1.DataSource = new BindingSource(o.GetKeyValuePairs(), null);
    comboBox1.DisplayMember = "Value";
    comboBox1.ValueMember = "Key";      
}

Наконец, чтобы вытащить выбранное значение, вы можете сделать это:

{
    // Get the selected item in the combobox
    KeyValuePair<int, string> selectedPair = (KeyValuePair<int, string>)comboBox1.SelectedItem;

    //I'm just sticking the values into text labels to demonstrate.
    lblSelectedKey.Text = selectedPair.Key.ToString();
    lblSelectedValue.Text = selectedPair.Value.ToString();
}

..и я не остановился на этом. Я расширил сам элемент управления ComboBox, поэтому теперь его привязка очень удобна.

    // Extends the Combobox control so that it can be automatically bound to an Enum
    // that has been decorated with Description attributes.
    // Sets the current value of the combobox to the value of the enum instance passed in.
    public static void BindToDecoratedEnum(this System.Windows.Forms.ComboBox cb, Enum e)
    {
        // Clear the combobox
        cb.DataSource = null;
        cb.Items.Clear();

        // Bind the combobox
        cb.DataSource = new System.Windows.Forms.BindingSource(e.GetKeyValuePairs(), null);
        cb.DisplayMember = "Value";
        cb.ValueMember = "Key";

        cb.Text = e.GetAttribute<DescriptionAttribute>().Description;
    }

Итак, теперь, всякий раз, когда я хочу заполнить DDL, я просто:

        myDDL.BindToDecoratedEnum(myEnumInstanceWithValue);

Код связывает его и выбирает элемент, который соответствует текущему значению переданного Enum.

Приветствуются комментарии и критика моей реализации (на самом деле, я был бы благодарен - как я уже сказал, я пытаюсь научиться...)

person SteveSteve    schedule 21.07.2011
comment
+1 хорошо, я уверен, что вы могли бы провести рефакторинг здесь и там, но в целом неплохо - person hunter; 22.07.2011

Еще одно предложение?

public static List<KeyValuePair<int, string>> GetKeyValuePairs<T>()
    {
        Type enumType = typeof(T);

        List<KeyValuePair<int, string>> ret = new List<KeyValuePair<int, string>>();
        foreach (Enum val in Enum.GetValues(enumType))
        {
            ret.Add(
                new KeyValuePair<int, string>(Convert.ToInt32(val),
                val.GetAttribute<DescriptionAttribute>().Description)
                );
        } return ret;
    }

Затем вы можете выполнить привязку, не создавая экземпляр перечисления. Это тип перечисления, о котором вам нужна информация, а не конкретный экземпляр)

ddlPes.DataSource = EnumHelper.GetKeyValuePairs<PesId>();
ddlPes.DataValueField = "key";
ddlPes.DataTextField = "value";
ddlPes.DataBind();
person like2175    schedule 03.02.2012