Как я могу иметь перечислимый привязан комбобокс с Custom строку форматирования для значений enum?
в должности Перечислить ToString, описан метод использования пользовательского атрибута DescriptionAttribute
такой:
Enum HowNice {
[Description("Really Nice")]
ReallyNice,
[Description("Kinda Nice")]
SortOfNice,
[Description("Not Nice At All")]
NotNice
}
и затем вы вызываете функцию GetDescription
, используя следующий синтаксис:
GetDescription<HowNice>(NotNice); // Returns "Not Nice At All"
но это мне не очень помогает когда я хочу просто заполнить ComboBox значениями перечисления, так как я не могу заставить ComboBox вызывать GetDescription
.
то, что я хочу имеет следующие требования:
- чтение
(HowNice)myComboBox.selectedItem
возвращает выбранное значение в качестве значения enum. - пользователь должен видеть удобные строки отображения, а не только имя значений перечисления. Поэтому вместо того, чтобы "
NotNice
", пользователь увидит "Not Nice At All
". - надеюсь, решение потребует минимальных изменений в код существующих перечислений.
очевидно, я мог бы реализовать новый класс для каждого enum, которое я создаю, и переопределить его ToString()
, но это много работы для каждого enum, и я бы предпочел избежать этого.
какие идеи?
Черт, я даже брошу в обнимашки как баунти :-)
21 ответов
вы можете написать TypeConverter, который читает указанные атрибуты, чтобы искать их в ваших ресурсах. Таким образом, вы получите многоязычную поддержку отображаемых имен без особых хлопот.
посмотрите на методы ConvertFrom/ConvertTo TypeConverter и используйте отражение для чтения атрибутов в вашем перечислении поля.
ComboBox
есть все, что вам нужно:FormattingEnabled
свойство, которое вы должны установить в true
и Format
событие, где вам нужно будет разместить желаемую логику форматирования. Таким образом,
myComboBox.FormattingEnabled = true;
myComboBox.Format += delegate(object sender, ListControlConvertEventArgs e)
{
e.Value = GetDescription<HowNice>((HowNice)e.Value);
}
нет! Перечисления-это примитивы ,а не объекты пользовательского интерфейса, заставляющие их служить пользовательскому интерфейсу.ToString () было бы довольно плохо с точки зрения дизайна. Вы пытаетесь решить не ту проблему: реальная проблема заключается в том, что вы не хотите, чтобы перечисление.ToString (), чтобы появиться в поле со списком!
теперь это действительно очень разрешимая проблема! Вы создаете объект пользовательского интерфейса для представления элементов поля со списком:
sealed class NicenessComboBoxItem
{
public string Description { get { return ...; } }
public HowNice Value { get; private set; }
public NicenessComboBoxItem(HowNice howNice) { Value = howNice; }
}
а затем просто добавьте экземпляры этого класса В элементы поля со списком соберите и установите эти свойства:
comboBox.ValueMember = "Value";
comboBox.DisplayMember = "Description";
TypeConverter. Думаю, это то, что я искал. Все град Симон Свенссон!
[TypeConverter(typeof(EnumToStringUsingDescription))]
Enum HowNice {
[Description("Really Nice")]
ReallyNice,
[Description("Kinda Nice")]
SortOfNice,
[Description("Not Nice At All")]
NotNice
}
все, что мне нужно изменить в моем текущем перечислении, это добавить эту строку перед их объявлением.
[TypeConverter(typeof(EnumToStringUsingDescription))]
как только я это сделаю, любое перечисление будет отображаться с помощью DescriptionAttribute
своего поля.
О, а TypeConverter
будет определено следующим образом:
public class EnumToStringUsingDescription : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return (sourceType.Equals(typeof(Enum)));
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return (destinationType.Equals(typeof(String)));
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (!destinationType.Equals(typeof(String)))
{
throw new ArgumentException("Can only convert to string.", "destinationType");
}
if (!value.GetType().BaseType.Equals(typeof(Enum)))
{
throw new ArgumentException("Can only convert an instance of enum.", "value");
}
string name = value.ToString();
object[] attrs =
value.GetType().GetField(name).GetCustomAttributes(typeof(DescriptionAttribute), false);
return (attrs.Length > 0) ? ((DescriptionAttribute)attrs[0]).Description : name;
}
}
это помогает мне с моим случаем ComboBox, но, очевидно, на самом деле не переопределяет ToString()
. Думаю, я пока удовлетворюсь этим...
используя пример перечисления:
using System.ComponentModel;
Enum HowNice
{
[Description("Really Nice")]
ReallyNice,
[Description("Kinda Nice")]
SortOfNice,
[Description("Not Nice At All")]
NotNice
}
создать расширение:
public static class EnumExtensions
{
public static string Description(this Enum value)
{
var enumType = value.GetType();
var field = enumType.GetField(value.ToString());
var attributes = field.GetCustomAttributes(typeof(DescriptionAttribute),
false);
return attributes.Length == 0
? value.ToString()
: ((DescriptionAttribute)attributes[0]).Description;
}
}
затем вы можете использовать что-то вроде следующего:
HowNice myEnum = HowNice.ReallyNice;
string myDesc = myEnum.Description();
см.:http://www.blackwasp.co.uk/EnumDescription.aspx для получения дополнительной информации. Кредит идет к Richrd Carr для решения
вы можете создать общую структуру, которую можно использовать для всех перечислений с описаниями. С неявными преобразованиями в класс и из класса ваши переменные по-прежнему работают как перечисление, за исключением метода ToString:
public struct Described<T> where T : struct {
private T _value;
public Described(T value) {
_value = value;
}
public override string ToString() {
string text = _value.ToString();
object[] attr =
typeof(T).GetField(text)
.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attr.Length == 1) {
text = ((DescriptionAttribute)attr[0]).Description;
}
return text;
}
public static implicit operator Described<T>(T value) {
return new Described<T>(value);
}
public static implicit operator T(Described<T> value) {
return value._value;
}
}
пример использования:
Described<HowNice> nice = HowNice.ReallyNice;
Console.WriteLine(nice == HowNice.ReallyNice); // writes "True"
Console.WriteLine(nice); // writes "Really Nice"
Я не думаю, что вы можете сделать это просто, без привязки к другому типу - по крайней мере, не удобно. Обычно, даже если вы не можете контролировать ToString()
, вы можете использовать TypeConverter
для пользовательского форматирования-но IIRC System.ComponentModel
материал не уважает это для перечислений.
вы можете привязаться к string[]
из описания, или что-то по существу, как пара ключ/значение? (desription / value) - что-то вроде:
class EnumWrapper<T> where T : struct
{
private readonly T value;
public T Value { get { return value; } }
public EnumWrapper(T value) { this.value = value; }
public string Description { get { return GetDescription<T>(value); } }
public override string ToString() { return Description; }
public static EnumWrapper<T>[] GetValues()
{
T[] vals = (T[])Enum.GetValues(typeof(T));
return Array.ConvertAll(vals, v => new EnumWrapper<T>(v));
}
}
а затем привязать к EnumWrapper<HowNice>.GetValues()
лучший способ сделать это-создать класс.
class EnumWithToString {
private string description;
internal EnumWithToString(string desc){
description = desc;
}
public override string ToString(){
return description;
}
}
class HowNice : EnumWithToString {
private HowNice(string desc) : base(desc){}
public static readonly HowNice ReallyNice = new HowNice("Really Nice");
public static readonly HowNice KindaNice = new HowNice("Kinda Nice");
public static readonly HowNice NotVeryNice = new HowNice("Really Mean!");
}
Я считаю, что это лучший способ, чтобы сделать это.
при заполнении в comboboxes будет показана красивая ToString, и тот факт, что никто не может сделать больше экземпляров вашего класса, по существу делает его перечислением.
p.s. возможно, потребуется несколько небольших исправлений синтаксиса, я не очень хорош с C#. (Java guy)
учитывая, что вы не хотите создавать класс для каждого перечисления, я бы рекомендовал создать словарь значения перечисления/отображаемого текста и привязать его вместо этого.
обратите внимание, что это зависит от методов метода GetDescription в исходном сообщении.
public static IDictionary<T, string> GetDescriptions<T>()
where T : struct
{
IDictionary<T, string> values = new Dictionary<T, string>();
Type type = enumerationValue.GetType();
if (!type.IsEnum)
{
throw new ArgumentException("T must be of Enum type", "enumerationValue");
}
//Tries to find a DescriptionAttribute for a potential friendly name
//for the enum
foreach (T value in Enum.GetValues(typeof(T)))
{
string text = value.GetDescription();
values.Add(value, text);
}
return values;
}
невозможно переопределить ToString () перечислений в C#. Однако, вы можете использовать методы расширения;
public static string ToString(this HowNice self, int neverUsed)
{
switch (self)
{
case HowNice.ReallyNice:
return "Rilly, rilly nice";
break;
...
конечно, вам придется сделать явный вызов метода, i.e;
HowNice.ReallyNice.ToString(0)
это не хорошее решение, с оператором switch и всем остальным, но оно должно работать и, надеюсь, whitout для многих перезаписей...
следуя ответу @scraimer, вот версия преобразователя типа перечисления в строку, который также поддерживает флаги:
/// <summary>
/// A drop-in converter that returns the strings from
/// <see cref="System.ComponentModel.DescriptionAttribute"/>
/// of items in an enumaration when they are converted to a string,
/// like in ToString().
/// </summary>
public class EnumToStringUsingDescription : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return (sourceType.Equals(typeof(Enum)));
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return (destinationType.Equals(typeof(String)));
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType.Equals(typeof(String)))
{
string name = value.ToString();
Type effectiveType = value.GetType();
if (name != null)
{
FieldInfo fi = effectiveType.GetField(name);
if (fi != null)
{
object[] attrs =
fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
return (attrs.Length > 0) ? ((DescriptionAttribute)attrs[0]).Description : name;
}
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
/// <summary>
/// Coverts an Enums to string by it's description. falls back to ToString.
/// </summary>
/// <param name="value">The value.</param>
/// <returns></returns>
public string EnumToString(Enum value)
{
//getting the actual values
List<Enum> values = EnumToStringUsingDescription.GetFlaggedValues(value);
//values.ToString();
//Will hold results for each value
List<string> results = new List<string>();
//getting the representing strings
foreach (Enum currValue in values)
{
string currresult = this.ConvertTo(null, null, currValue, typeof(String)).ToString();;
results.Add(currresult);
}
return String.Join("\n",results);
}
/// <summary>
/// All of the values of enumeration that are represented by specified value.
/// If it is not a flag, the value will be the only value retured
/// </summary>
/// <param name="value">The value.</param>
/// <returns></returns>
private static List<Enum> GetFlaggedValues(Enum value)
{
//checking if this string is a flaged Enum
Type enumType = value.GetType();
object[] attributes = enumType.GetCustomAttributes(true);
bool hasFlags = false;
foreach (object currAttibute in attributes)
{
if (enumType.GetCustomAttributes(true)[0] is System.FlagsAttribute)
{
hasFlags = true;
break;
}
}
//If it is a flag, add all fllaged values
List<Enum> values = new List<Enum>();
if (hasFlags)
{
Array allValues = Enum.GetValues(enumType);
foreach (Enum currValue in allValues)
{
if (value.HasFlag(currValue))
{
values.Add(currValue);
}
}
}
else//if not just add current value
{
values.Add(value);
}
return values;
}
}
и метод расширения для использования:
/// <summary>
/// Converts an Enum to string by it's description. falls back to ToString
/// </summary>
/// <param name="enumVal">The enum val.</param>
/// <returns></returns>
public static string ToStringByDescription(this Enum enumVal)
{
EnumToStringUsingDescription inter = new EnumToStringUsingDescription();
string str = inter.EnumToString(enumVal);
return str;
}
Я бы написал общий класс для использования с любым типом. Я использовал что-то подобное в прошлом:
public class ComboBoxItem<T>
{
/// The text to display.
private string text = "";
/// The associated tag.
private T tag = default(T);
public string Text
{
get
{
return text;
}
}
public T Tag
{
get
{
return tag;
}
}
public override string ToString()
{
return text;
}
// Add various constructors here to fit your needs
}
кроме того, вы можете добавить статический "заводской метод", чтобы создать список элементов combobox с учетом типа перечисления (почти такой же, как метод GetDescriptions, который у вас есть). Это избавит вас от необходимости реализовывать одну сущность для каждого типа перечисления, а также предоставит хорошее / логическое место для вспомогательного метода "GetDescriptions" (лично я бы назвал его FromEnum (T obj)...
создайте коллекцию, содержащую то, что вам нужно (например, простые объекты, содержащие Value
свойство, содержащее HowNice
значения enum и Description
свойство, содержащее GetDescription<HowNice>(Value)
и databind комбо к этой коллекции.
вот так:
Combo.DataSource = new EnumeratedValueCollection<HowNice>();
Combo.ValueMember = "Value";
Combo.DisplayMember = "Description";
когда у вас есть класс сборник такой:
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Whatever.Tickles.Your.Fancy
{
public class EnumeratedValueCollection<T> : ReadOnlyCollection<EnumeratedValue<T>>
{
public EnumeratedValueCollection()
: base(ListConstructor()) { }
public EnumeratedValueCollection(Func<T, bool> selection)
: base(ListConstructor(selection)) { }
public EnumeratedValueCollection(Func<T, string> format)
: base(ListConstructor(format)) { }
public EnumeratedValueCollection(Func<T, bool> selection, Func<T, string> format)
: base(ListConstructor(selection, format)) { }
internal EnumeratedValueCollection(IList<EnumeratedValue<T>> data)
: base(data) { }
internal static List<EnumeratedValue<T>> ListConstructor()
{
return ListConstructor(null, null);
}
internal static List<EnumeratedValue<T>> ListConstructor(Func<T, string> format)
{
return ListConstructor(null, format);
}
internal static List<EnumeratedValue<T>> ListConstructor(Func<T, bool> selection)
{
return ListConstructor(selection, null);
}
internal static List<EnumeratedValue<T>> ListConstructor(Func<T, bool> selection, Func<T, string> format)
{
if (null == selection) selection = (x => true);
if (null == format) format = (x => GetDescription<T>(x));
var result = new List<EnumeratedValue<T>>();
foreach (T value in System.Enum.GetValues(typeof(T)))
{
if (selection(value))
{
string description = format(value);
result.Add(new EnumeratedValue<T>(value, description));
}
}
return result;
}
public bool Contains(T value)
{
return (Items.FirstOrDefault(item => item.Value.Equals(value)) != null);
}
public EnumeratedValue<T> this[T value]
{
get
{
return Items.First(item => item.Value.Equals(value));
}
}
public string Describe(T value)
{
return this[value].Description;
}
}
[System.Diagnostics.DebuggerDisplay("{Value} ({Description})")]
public class EnumeratedValue<T>
{
private T value;
private string description;
internal EnumeratedValue(T value, string description) {
this.value = value;
this.description = description;
}
public T Value { get { return this.value; } }
public string Description { get { return this.description; } }
}
}
как вы можете видеть, эта коллекция легко настраивается с помощью лямбды, чтобы выбрать подмножество вашего перечислителя и / или реализовать пользовательский форматирование в string
вместо
вы можете использовать PostSharp для целевого перечисления.ToString и добавить aditionall код, который вы хотите. Это не требует никаких изменений кода.
что вам нужно, это превратить перечисление в ReadonlyCollection и привязать коллекцию к combobox (или любой паре ключ-значение включен контроль в этом отношении)
во-первых, вам нужен класс, содержащий элементы списка. Поскольку все, что вам нужно, это пара int / string, я предлагаю использовать интерфейс и комбо базового класса, чтобы вы могли реализовать функциональность в любом объекте:
public interface IValueDescritionItem
{
int Value { get; set;}
string Description { get; set;}
}
public class MyItem : IValueDescritionItem
{
HowNice _howNice;
string _description;
public MyItem()
{
}
public MyItem(HowNice howNice, string howNice_descr)
{
_howNice = howNice;
_description = howNice_descr;
}
public HowNice Niceness { get { return _howNice; } }
public String NicenessDescription { get { return _description; } }
#region IValueDescritionItem Members
int IValueDescritionItem.Value
{
get { return (int)_howNice; }
set { _howNice = (HowNice)value; }
}
string IValueDescritionItem.Description
{
get { return _description; }
set { _description = value; }
}
#endregion
}
вот интерфейс и пример класса, который реализует его.Уведомление что ключ класса строго типизирован для перечисления и что ivaluedescritionitem proprties реализованы явно (поэтому класс может иметь любые свойства, и вы можете выбрать те, которые реализуют пару ключ/значение.
Теперь класс EnumToReadOnlyCollection:
public class EnumToReadOnlyCollection<T,TEnum> : ReadOnlyCollection<T> where T: IValueDescritionItem,new() where TEnum : struct
{
Type _type;
public EnumToReadOnlyCollection() : base(new List<T>())
{
_type = typeof(TEnum);
if (_type.IsEnum)
{
FieldInfo[] fields = _type.GetFields();
foreach (FieldInfo enum_item in fields)
{
if (!enum_item.IsSpecialName)
{
T item = new T();
item.Value = (int)enum_item.GetValue(null);
item.Description = ((ItemDescription)enum_item.GetCustomAttributes(false)[0]).Description;
//above line should be replaced with proper code that gets the description attribute
Items.Add(item);
}
}
}
else
throw new Exception("Only enum types are supported.");
}
public T this[TEnum key]
{
get
{
return Items[Convert.ToInt32(key)];
}
}
}
таким образом, все, что вам нужно в вашем коде:
private EnumToReadOnlyCollection<MyItem, HowNice> enumcol;
enumcol = new EnumToReadOnlyCollection<MyItem, HowNice>();
comboBox1.ValueMember = "Niceness";
comboBox1.DisplayMember = "NicenessDescription";
comboBox1.DataSource = enumcol;
помните, что ваша коллекция набрана с помощью MyItem, поэтому значение combobox должно возвращать значение перечисления, если вы привязываетесь к соответствующий proprtie.
я добавил свойство t this[Enum t], чтобы сделать коллекцию еще более полезной, чем простое комбо, например textBox1.Text = enumcol[HowNice.ReallyNice].NicenessDescription;
вы, конечно, можете превратить MyItem в класс ключа / значения, используемый только для этого puprose, эффективно пропуская MyItem в аргументах типа EnumToReadnlyCollection в целом, но тогда вы будете вынуждены пойти с int для ключа (что означает получение поле combobox1.SelectedValue вернет int, а не тип перечисления). Вы обходите это, если создаете класс KeyValueItem для замены MyItem и так далее и тому подобное...
извините за получение этой старой нити.
Я бы пошел следующим образом, чтобы локализовать перечисление, поскольку оно может отображать значимые и локализованные значения пользователю, а не только описание, через текстовое поле dropdownlist в этом примере.
во-первых, я создаю простой метод OwToStringByCulture для получения локализованных строк из файла глобальных ресурсов, в этом примере это BiBongNet.resx в папке App_GlobalResources. Внутри этого файла ресурсов убедитесь, что у вас есть все строки такие же, как значения перечисления (ReallyNice, SortOfNice, NotNice). В этом методе я передаю параметр: resourceClassName, который обычно является именем файла ресурсов.
затем я создаю статический метод для заполнения выпадающего списка перечислением в качестве источника данных, называемого OwFillDataWithEnum. Этот метод можно использовать с любым перечислением позже.
затем на странице с выпадающим списком под названием DropDownList1 я установил в Page_Load следующую простую строку кода чтобы заполнить перечисление в раскрывающемся списке.
BiBongNet.OwFillDataWithEnum<HowNice>(DropDownList1, "BiBongNet");
вот именно. Я думаю, что с помощью некоторых простых методов, подобных этим, вы можете заполнить любой элемент управления списком любым перечислением, не только описательными значениями, но и локализованным текстом для отображения. Вы можете сделать все эти методы, как методы расширения для лучшего использования.
надеюсь, что это поможет. Поделиться, чтобы получить общий доступ!
вот методы:
public class BiBongNet
{
enum HowNice
{
ReallyNice,
SortOfNice,
NotNice
}
/// <summary>
/// This method is for filling a listcontrol,
/// such as dropdownlist, listbox...
/// with an enum as the datasource.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="ctrl"></param>
/// <param name="resourceClassName"></param>
public static void OwFillDataWithEnum<T>(ListControl ctrl, string resourceClassName)
{
var owType = typeof(T);
var values = Enum.GetValues(owType);
for (var i = 0; i < values.Length; i++)
{
//Localize this for displaying listcontrol's text field.
var text = OwToStringByCulture(resourceClassName, Enum.Parse(owType, values.GetValue(i).ToString()).ToString());
//This is for listcontrol's value field
var key = (Enum.Parse(owType, values.GetValue(i).ToString()));
//add values of enum to listcontrol.
ctrl.Items.Add(new ListItem(text, key.ToString()));
}
}
/// <summary>
/// Get localized strings.
/// </summary>
/// <param name="resourceClassName"></param>
/// <param name="resourceKey"></param>
/// <returns></returns>
public static string OwToStringByCulture(string resourceClassName, string resourceKey)
{
return (string)HttpContext.GetGlobalResourceObject(resourceClassName, resourceKey);
}
}
Enum HowNice {
[Description("Really Nice")]
ReallyNice,
[Description("Kinda Nice")]
SortOfNice,
[Description("Not Nice At All")]
NotNice
}
чтобы решить эту проблему, вы должны использовать метод расширения и массив строк, например:
Enum HowNice {
ReallyNice = 0,
SortOfNice = 1,
NotNice = 2
}
internal static class HowNiceIsThis
{
const String[] strings = { "Really Nice", "Kinda Nice", "Not Nice At All" }
public static String DecodeToString(this HowNice howNice)
{
return strings[(int)howNice];
}
}
простой код и быстрое декодирование.
я попробовал этот подход, и он сработал для меня.
Я создал класс-оболочку для перечислений и перегрузил неявный оператор, чтобы я мог назначить его переменным перечисления (в моем случае мне пришлось привязать объект к ComboBox
значение).
вы можете использовать отражение для форматирования значений перечисления так, как вы хотите, в моем случае я получаю DisplayAttribute
из значений перечисления (если они существуют).
надеюсь, что это помогает.
public sealed class EnumItem<T>
{
T value;
public override string ToString()
{
return Display;
}
public string Display { get; private set; }
public T Value { get; set; }
public EnumItem(T val)
{
value = val;
Type en = val.GetType();
MemberInfo res = en.GetMember(val.ToString())?.FirstOrDefault();
DisplayAttribute display = res.GetCustomAttribute<DisplayAttribute>();
Display = display != null ? String.Format(display.Name, val) : val.ToString();
}
public static implicit operator T(EnumItem<T> val)
{
return val.Value;
}
public static implicit operator EnumItem<T>(T val)
{
return new EnumItem<T>(val);
}
}
EDIT:
на всякий случай, я использую следующую функцию, чтобы получить enum
значения, которые я использую для DataSource
на ComboBox
public static class Utils
{
public static IEnumerable<EnumItem<T>> GetEnumValues<T>()
{
List<EnumItem<T>> result = new List<EnumItem<T>>();
foreach (T item in Enum.GetValues(typeof(T)))
{
result.Add(item);
}
return result;
}
}
после GetDescription
метод (он должен быть глобальным статическим), вы можете использовать это через метод расширения:
public static string ToString(this HowNice self)
{
return GetDescription<HowNice>(self);
}
Enum HowNice {
[StringValue("Really Nice")]
ReallyNice,
[StringValue("Kinda Nice")]
SortOfNice,
[StringValue("Not Nice At All")]
NotNice
}
Status = ReallyNice.GetDescription()
Вы можете определить перечисление как
Enum HowNice {
[StringValue("Really Nice")]
ReallyNice,
[StringValue("Kinda Nice")]
SortOfNice,
[StringValue("Not Nice At All")]
NotNice
}
и затем использовать HowNice.GetStringValue()
.