2013-08-09 14 views
5

Muszę utworzyć SelectList z dowolnego Enum w moim projekcie.Generic Enum to SelectList extension method

Mam poniższy kod, który utworzyłem listę wyboru z określonego wyliczenia, ale chciałbym uczynić metodę rozszerzenia dla każdego wyliczenia. Ten przykład pobiera wartość DescriptionAttribute na każdej wartości Enum

var list = new SelectList(
      Enum.GetValues(typeof(eChargeType)) 
      .Cast<eChargeType>() 
      .Select(n => new 
       { 
        id = (int)n, 
        label = n.ToString() 
       }), "id", "label", charge.type_id); 

Wywołanie this post, jak mam postępować?

public static void ToSelectList(this Enum e) 
{ 
    // code here 
} 

Odpowiedz

4

To, co myślę, że zmagasz się, to odzyskanie opisu. Jestem pewien, że gdy już masz te, które możesz zdefiniować swoją ostateczną metodę, która daje dokładny wynik.

Po pierwsze, jeśli zdefiniujesz metodę rozszerzenia, działa ona na wartości wyliczenia, a nie na samym typie wyliczenia. I myślę, że dla łatwego użycia, chciałbyś wywołać metodę na typie (jak metoda statyczna). Niestety nie możesz tego zdefiniować.

Co możesz zrobić, to: Najpierw zdefiniować metodę, która pobiera opis wartości enum, jeśli ma jedną:

public static string GetDescription(this Enum value) { 
    string description = value.ToString(); 
    FieldInfo fieldInfo = value.GetType().GetField(description); 
    DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false); 

    if (attributes != null && attributes.Length > 0) { 
     description = attributes[0].Description; 
    } 
    return description; 
} 

Następnie zdefiniować metodę, która przyjmuje wszystkie wartości wyliczenia i korzystania z poprzedniej metody, aby sprawdzić wartość którą chcesz pokazać i zwrócić tę listę. Ogólny argument można wywnioskować.

public static List<KeyValuePair<TEnum, string>> ToEnumDescriptionsList<TEnum>(this TEnum value) { 
    return Enum 
     .GetValues(typeof(TEnum)) 
     .Cast<TEnum>() 
     .Select(x => new KeyValuePair<TEnum, string>(x, ((Enum)((object)x)).GetDescription())) 
     .ToList(); 
} 

Wreszcie, dla ułatwienia użytkowania, metoda wywoływania go bezpośrednio bez wartości. Ale ogólny argument nie jest opcjonalny.

public static List<KeyValuePair<TEnum, string>> ToEnumDescriptionsList<TEnum>() { 
    return ToEnumDescriptionsList<TEnum>(default(TEnum)); 
} 

Teraz możemy używać go tak:

enum TestEnum { 
    [Description("My first value")] 
    Value1, 
    Value2, 
    [Description("Last one")] 
    Value99 
} 

var items = default(TestEnum).ToEnumDescriptionsList(); 
// or: TestEnum.Value1.ToEnumDescriptionsList(); 
// Alternative: EnumExtensions.ToEnumDescriptionsList<TestEnum>() 
foreach (var item in items) { 
    Console.WriteLine("{0} - {1}", item.Key, item.Value); 
} 
Console.ReadLine(); 

które wyjścia:

Value1 - My first value 
Value2 - Value2 
Value99 - Last one 
+0

Dzięki za szczegółowe odpowiedzi:) Szczerze mówiąc, chcę po prostu czegoś naprawdę prostego (jeśli to możliwe), więc mogę użyć 'eChargeType.ToSelectList()' - Zmniejszyłem złożoność mojego przykładu, aby wykluczyć 'DescriptionAttribute', ponieważ może to być niepotrzebnym zamieszaniem. Dzięki! – Jimbo

+0

Niestety, 'eChargeType.ToSelectList()' nie jest możliwe, ponieważ chcesz dodać metodę statyczną do typu wyliczeniowego. A to nie jest możliwe, ale możesz gdzieś stworzyć statyczną metodę pomocnika. – Maarten

+0

W odniesieniu do powyższego komentarza, czy możesz rzucić światło na to, co autor tego postu miał nadzieję osiągnąć? http://stackoverflow.com/a/276589/175893 Myślałem, że jesteśmy w tej samej misji! Dzięki – Jimbo

1

późno do partii, ale ponieważ nie jest akceptowana odpowiedź i może pomóc innym:

Jak wspomniano w @Maarten, metoda rozszerzenia działa na wartość wyliczenia, a nie na typ enum itelf, więc z soultion Maarteen mogą tworzyć obojętne lub domyślną wartość wywołać metodę rozszerzenia na, jednak może się okazać, jak ja, że ​​łatwiej jest po prostu użyć statycznej metody pomocnika tak:

public static class EnumHelper 
{ 
    public static SelectList GetSelectList<T>(string selectedValue, bool useNumeric = false) 
    { 
     Type enumType = GetBaseType(typeof(T)); 

     if (enumType.IsEnum) 
     { 
      var list = new List<SelectListItem>(); 

      // Add empty option 
      list.Add(new SelectListItem { Value = string.Empty, Text = string.Empty }); 

      foreach (Enum e in Enum.GetValues(enumType)) 
      { 
       list.Add(new SelectListItem { Value = useNumeric ? Convert.ToInt32(e).ToString() : e.ToString(), Text = e.Description() }); 
      } 

      return new SelectList(list, "Value", "Text", selectedValue); 
     } 

     return null; 
    } 

    private static bool IsTypeNullable(Type type) 
    { 
     return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)); 
    } 

    private static Type GetBaseType(Type type) 
    { 
     return IsTypeNullable(type) ? type.GetGenericArguments()[0] : type; 
    } 

You stworzyłoby listy wyboru takiego:

viewModel.ProvinceSelect = EnumHelper.GetSelectList<Province>(model.Province); 

lub za pomocą opcjonalnych wartości numeryczne zamiast strun:

viewModel.MonthSelect = EnumHelper.GetSelectList<Month>(model.Month,true); 

podstawową ideą tego dostałem od here, chociaż zmieniłem go, aby odpowiadał moim potrzebom. Jedną z rzeczy, którą dodałem, była możliwość opcjonalnego użycia wartości int dla wartości.Dodałem też rozszerzenie enum uzyskać atrybut opisu, który jest oparty na this blogu:

public static class EnumExtensions 
{ 
    public static string Description(this 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(); 
    }  
} 
0

Ponieważ wyliczenia nie może mieć rozszerzenia przypięte do całej kolekcji wygodny sposób rozszerzać klasę bazową Enum jest w klasa statyczna. Pozwoli to kod zwięzłe, takie jak:

Enum<MyCustomEnumType>.GetSelectItems(); 

co można osiągnąć za pomocą następującego kodu:

public static class Enum<T> 
{ 
    public static SelectListItem[] GetSelectItems() 
    { 
     Type type = typeof(T); 
     return 
      Enum.GetValues(type) 
       .Cast<object>() 
       .Select(v => new SelectListItem() { Value = v.ToString(), Text = Enum.GetName(type, v) }) 
       .ToArray(); 
    } 
} 

Ponieważ wyliczenia nie mają wspólny typ interfejsu niewłaściwe jest możliwe, ale nazwa klasy Enum powinny rozwiać wszelkie zamieszanie.

0

Oto poprawiony [typ lanego wartość int] i uproszczone [korzysta toString override zamiast getName] wersję Nathaniels odpowiedź, która zwraca listę zamiast tablicy:

public static class Enum<T> 
{ 
    //usage: var lst = Enum<myenum>.GetSelectList(); 
    public static List<SelectListItem> GetSelectList() 
    { 
     return Enum.GetValues(typeof(T)) 
       .Cast<object>() 
       .Select(i => new SelectListItem() 
          { Value = ((int)i).ToString() 
           ,Text = i.ToString() }) 
       .ToList(); 
    } 

}