2009-08-25 13 views
19

mam wyliczenie jakJak mieć przyjazne nazwy dla wyliczeń?

Enum Complexity 
{ 
    NotSoComplex, 
    LittleComplex, 
    Complex, 
    VeryComplex 
} 

I chcę używać go na liście rozwijanej, ale nie chcę, aby zobaczyć takie nazwiska Wielbłąd listy (wygląda bardzo dziwne dla użytkowników). Zamiast tego chciałbym mieć w normalnym brzmieniu, jak Nie tak skomplikowanej Małej kompleksu (ETC)

Również moja aplikacja jest multi-lang i chciałbym być w stanie wyświetlać te struny zlokalizowane i używam helper, TranslationHelper (string strID), który daje mi zlokalizowaną wersję dla id łańcucha.

Mam roztwór roboczy, ale nie bardzo elegancki: utworzyć klasy pomocnika do wyliczenia, z jednym Complexity członkiem i toString() nadpisane, jak poniżej (kod uproszczony)

public class ComplexityHelper 
{ 
    public ComplexityHelper(Complexity c, string desc) 
    { m_complex = c; m_desc=desc; } 

    public Complexity Complexity { get { ... } set {...} } 
    public override ToString() { return m_desc; } 

    //Then a static field like this 

    private static List<Complexity> m_cxList = null; 

    // and method that returns the status lists to bind to DataSource of lists 
    public static List<ComplexityHelper> GetComplexities() 
    { 
     if (m_cxList == null) 
     { 
      string[] list = TranslationHelper.GetTranslation("item_Complexities").Split(','); 
      Array listVal = Enum.GetValues(typeof(Complexities)); 
      if (list.Length != listVal.Length) 
       throw new Exception("Invalid Complexities translations (item_Complexities)"); 
      m_cxList = new List<Complexity>(); 
      for (int i = 0; i < list.Length; i++) 
      { 
      Complexity cx = (ComplexitylistVal.GetValue(i); 
      ComplexityHelper ch = new ComplexityHelper(cx, list[i]); 
      m_cxList.Add(ch); 
      } 
     } 
     return m_cxList; 
    } 
} 

While wykonalne , Nie jestem z tego zadowolony, ponieważ muszę go kodować podobnie do różnych wyrażeń, które muszę użyć w listach wyboru.

Czy ktoś ma sugestię prostszego lub bardziej ogólnego rozwiązania?

Dzięki Bogdan

+0

See

Odpowiedz

1

Dziękuję wszystkim za wszystkie odpowiedzi. W końcu użyłem kombinacji Rex M i adrianbanks i dodałem własne ulepszenia, aby uprościć wiązanie z ComboBox.

Zmiany były potrzebne, ponieważ podczas pracy nad kodem zdawałem sobie sprawę, że czasami potrzebuję móc wykluczyć jeden element wyliczeniowy z kombinacji. E.g.

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 
} 

Więc czasami chcę listy wyboru, aby pokazać wszystkim, ale AllComplexities (w dodatku - zmieniać na moduły) i inny czas, aby zobaczyć wszystko (filtrów)

Oto co zrobiłem:

  1. Stworzyłem metodę rozszerzenia, która wykorzystuje atrybut Opis Atrybut jako klucz wyszukiwania lokalizacji. Jeśli brakuje atrybutu Opis, utworzę klucz wyszukiwania odnośnika jako EnumName_ EnumValue. Na koniec, jeśli brakuje tłumaczenia, po prostu dzielę nazwę na podstawie camelcase, aby oddzielić słowa, jak pokazują adrianbanks. BTW, TranslationHelper jest wrapper wokół resourceMgr.GetString (...)

pełny kod znajduje się poniżej

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; 
} 

utworzonego ogólną klasę pomocniczego w oparciu o Enum, które umożliwiają w prosty sposób powiązać wyliczenia do źródła danych

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(); } 
    } 

} 

Następnie utworzona ogólna statyczna metoda zwracająca listę>, jak poniżej:

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; 
} 

oraz owijkę do listy powrócić ze wszystkimi członkami

public static List<LocalizableEnum<T>> GetEnumList<T>() 
{ 
     return GetEnumList<T>(null); 
} 

Teraz postawmy wszystkie rzeczy razem i wiązać się z faktyczną combo:

// 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 

na czytanie wybranych wartości i używać go, używam poniżej kod

Complexity selComplexity = (Complexity)comboComplexity.SelectedValue; 
60

Podstawowe przyjazne nazwy

użyć Description attribute: *

enum MyEnum 
{ 
    [Description("This is black")] 
    Black, 
    [Description("This is white")] 
    White 
} 

i poręczny sposób na przedłużenie teksty stałe:

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; 
} 

Używane tak:

MyEnum val = MyEnum.Black; 
Console.WriteLine(val.GetDescription()); //writes "This is black" 

(Uwaga ta nie dokładnie pracować flag bitowych ...)

Dla lokalizacji

Istnieje ugruntowane wzór w NET obsługę wielu języków na wartości ciągu - używać resource file i rozwinąć metodę rozszerzenia do odczytu z pliku zasobów:

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; 
} 

Za każdym razem, gdy możemy wykorzystać istniejącą funkcjonalność BCL, aby osiągnąć to, czego chcemy, jest to zdecydowanie pierwsza droga do odkrycia. Minimalizuje to złożoność i wykorzystuje wzorce znane wielu innym programistom.

Wszystko razem

Aby uzyskać to wiązać się z DropDownList, prawdopodobnie chcesz śledzić wartości realne enum w naszej kontroli i ograniczyć przetłumaczony, przyjazną nazwę cukru wizualnej.Możemy to zrobić za pomocą anonimowego rodzaj i właściwości dataField na liście:

<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(); 

Załóżmy rozbić tę linię DataSource:

  • pierwsze nazywamy Enum.GetValues(typeof(MyEnum)), który dostaje nam luźno wpisane Array wartości
  • Następny nazywamy OfType<MyEnum>() który konwertuje tablicę do IEnumerable<MyEnum>
  • wówczas nazywamy Select() i zapewniają lambda, który wystaje nowa obiekt z dwoma polami, opisem i wartością.

Właściwości DataTextField i DataValueField są poddawane refleksyjnej ocenie przy użyciu databind-time, dzięki czemu będą wyszukiwać pola w DataItem z pasującymi nazwami.

- Uwaga w głównym artykule autor napisał własną klasę DescriptionAttribute, która jest niepotrzebna, tak jak już istnieje w standardowych bibliotekach .NET. -

+5

Dla lokalizacji, można pominąć atrybuty opis i po prostu użyć „MyEnum.Blac k "i" MyEnum.White "jako nazwy zasobów. – stevemegson

+0

@stevemegson, który z pewnością zadziała! Jestem jednak wielkim purystą z SoC/SRP, a dla mnie podwójne wyliczenie enum jako wartości programowej i wielojęzycznego klucza zasobów uruchamia ostrzegawcze dzwony :) –

+0

Witam, Chciałbym użyć wiązanie takie jak comboComplexity.DataSource = Enum.GetValues ​​(typeof (Complexity)); ale przy użyciu tego ja wciąż w kombi nazwy wartości wyliczenia, a nie ich opisy Ponadto, przemianowany pobierzOpis do toString(), mając nadzieję, że kombi będzie użyć dla nazwy enum, ale bez powodzenia Wszelkie sugestia? Dzięki – bzamfir

1

używam następujące klasy

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."); 
    } 

który odczytuje atrybut o nazwie opis

public enum PuppyType 
{ 
    [Description("Cute Puppy")] 
    CutePuppy = 0, 
    [Description("Silly Puppy")] 
    SillyPuppy 
} 
4

Stosowanie atrybutów jak w innych odpowiedzi to dobra droga, ale jeśli po prostu chcesz użyć tekstu z wartości enum, poniższy kod zostanie podzielony na podstawie wielbłądzowej obudowy wartości:

public static string GetDescriptionOf(Enum enumType) 
{ 
    Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled); 
    return capitalLetterMatch.Replace(enumType.ToString(), " $&"); 
} 

Wywołanie GetDescriptionOf(Complexity.NotSoComplex) spowoduje zwrócenie Not So Complex. Można go użyć z wartością wyliczeniową dowolną wartość.

Aby uczynić go bardziej użyteczne, można zrobić to metodę rozszerzenia:

public static string ToFriendlyString(this Enum enumType) 
{ 
    Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled); 
    return capitalLetterMatch.Replace(enumType.ToString(), " $&"); 
} 

CAL można teraz połączyć go za pomocą Complexity.NotSoComplex.ToFriendlyString() wrócić Not So Complex.


EDIT: Właśnie zauważyłem, że w swoim pytaniu wspomnieć, że trzeba zlokalizować tekst. W takim przypadku użyłbym atrybutu, aby zawierał klucz do wyszukania zlokalizowanej wartości, ale domyślnie do metody przyjaznej dla użytkownika w ostateczności, jeśli zlokalizowanego tekstu nie można znaleźć.Można by określić Ci stałe teksty tak:

enum Complexity 
{ 
    [LocalisedEnum("Complexity.NotSoComplex")] 
    NotSoComplex, 
    [LocalisedEnum("Complexity.LittleComplex")] 
    LittleComplex, 
    [LocalisedEnum("Complexity.Complex")] 
    Complex, 
    [LocalisedEnum("Complexity.VeryComplex")] 
    VeryComplex 
} 

byś również potrzebują tego kodu:

[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; 
    } 
} 

aby uzyskać zlokalizowanych opisów, byś potem zadzwonić:

Complexity.NotSoComplex.ToLocalisedString() 

ten ma kilka awarie:

  • jeśli enu m ma atrybut LocalisedEnum zdefiniowane, to użyj klawisza, aby sprawdzić przetłumaczony tekst
  • jeśli enum ma atrybut LocalisedEnum zdefiniowane, ale nie zlokalizowane tekst zostanie znaleziony, to domyślnie pomocą wielbłądziej rozdzielonego metodę
  • if enum nie posiada atrybut LocalisedEnum zdefiniowane, będzie korzystał z wielbłądziej rozdzielonego metodę
  • na jakiekolwiek błędy, to domyślne do toString wartość enum
+0

Witam Dziękuję za odpowiedź. Wypróbowałem twoją sugestię i próbowałem powiązać combo do wyliczenia z kodem takim jak ten comboComplexity.DataSource = Enum.GetValues ​​(typeof (Complexity)); Ale to sprawiło, że lista wyświetlała tylko domyślne nazwy wyliczeniowe Zmieniono również nazwę ToLocalizedString() na ToString() (wiedząc, że jest to faktycznie wywoływane przez Combo), ale nadal nie działało. Jakieś sugestie? Chciałbym proste powiązanie w ten sposób, ponieważ z wyliczeniem z wartościami 20+, dodawanie wszystkich wartości jeden po drugim będzie bólem Dzięki – bzamfir

+0

Zmieniając nazwę ToLocalizedString() na ToString(), nie zastąpiłeś ToString() metoda wyliczenia. Zamiast tego stworzyłeś metodę rozszerzenia o tej samej nazwie co istniejąca metoda wyliczania ToString().Wywołanie ToString() wywoła istniejącą metodę, a nie metodę rozszerzenia. Będziesz musiał uzyskać zlokalizowane ciągi dla każdej wartości wyliczeniowej i databind do tego zestawu łańcuchów. – adrianbanks

Powiązane problemy