Несколько описаний перечислений
Я определил следующее enum
:
public enum DeviceType
{
[Description("Set Top Box")]
Stb = 1,
Panel = 2,
Monitor = 3,
[Description("Wireless Keyboard")]
WirelessKeyboard = 4
}
Я использую атрибут Description
, чтобы позволить мне вытащить более читаемую пользователем версию перечисления для отображения в пользовательском интерфейсе. Я получаю описание, используя следующий код:
var fieldInfo = DeviceType.Stb.GetType().GetField(DeviceType.Stb.ToString());
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
var description = (attributes.Length > 0 ? attributes[0].Description : DeviceType.Stb.ToString());
Приведенный выше код даст мне: description = "Set Top Box"
. Если атрибут Description
не установлен, он даст мне строковое значение перечисления.
Теперь я хочу добавить второй/пользовательский атрибут для каждого из перечислений (так называемый "Value" для примеров). например:
public enum DeviceType
{
[Description("Set Top Box")]
[Value("19.95")]
Stb = 1,
[Value("99")]
Panel = 2,
[Value("199.99")]
Monitor = 3,
[Description("Wireless Keyboard")]
[Value("20")]
WirelessKeyboard = 4
}
Мне нужно будет вытащить новый атрибут Value
так же, как в настоящее время с атрибутом Description
.
Можно ли расширить существующий атрибут Description
, чтобы каким-то образом включить новый атрибут Value
, или лучше всего создать новый атрибут отдельно?
Ответы
Ответ 1
Создайте новый атрибут, отдельно названный DeviceInformation...
[AttributeUsage(AttributeTargets.All)]
public class DeviceInformationAttribute : DescriptionAttribute
{
public DeviceInformationAttribute(string description, string value)
{
this.Description = description;
this.Value = value;
}
public string Description { get; set; }
public string Value { get; set; }
}
Вы также можете использовать метод расширения для извлечения значения любого атрибута
static void Main(string[] args)
{
var info = DeviceType.Stb.GetAttribute<DeviceInformationAttribute>();
Console.WriteLine("Description: {0}\nValue:{1}",info.Description, info.Value);
}
public static class Extensions
{
public static TAttribute GetAttribute<TAttribute>(this Enum enumValue)
where TAttribute : Attribute
{
return enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<TAttribute>();
}
}
public enum DeviceType
{
[DeviceInformation("foobar", "100")]
Stb = 1,
}
Изменить
В ответ на комментарий
@Aydin Adn Мне очень нравится использование метода расширения, очень приятно! У вас есть решение для случая DeviceType.Panel, который не имеет описания, но нуждается в атрибуте Value? (см. комментарии к ответу Патрика)
[AttributeUsage(AttributeTargets.All)]
public class DeviceInformationAttribute : Attribute
{
public DeviceInformationAttribute(string description)
{
this.Description = description;
}
public DeviceInformationAttribute(decimal value)
{
this.Description = string.Empty;
this.Value = value;
}
public DeviceInformationAttribute(string description, decimal value)
{
this.Description = description;
this.Value = value;
}
public string Description { get; set; }
public decimal Value { get; set; }
}
Ответ 2
Да, это довольно легко сделать. Просто выведите существующий класс DescriptionAttribute
:
[AttributeUsageAttribute(AttributeTargets.All)]
public class DescriptionWithValueAttribute : DescriptionAttribute
{
public DescriptionWithValueAttribute(string description, string value) : base(description)
{
this.Value = value;
}
public string Value { get; private set; }
}
Затем вы можете использовать его следующим образом:
public enum DeviceType
{
[DescriptionWithValue("Set Top Box", "19.95")]
Stb = 1,
}
Ваш код для извлечения атрибутов останется практически таким же, просто замените имена типов.
Ответ 3
Что вы хотите сделать: Создайте атрибут, чтобы описать enume более конкретным: вот как вы можете это сделать:
public class EnumValue : Attribute
{
public Decimal Value { get; private set; }
public EnumValue(Decimal value)
{
this.Value = value;
}
}
Это можно использовать с помощью этого метода расширения:
private static Decimal GetEnumCustomAttribute(this Enum leEnum, Typ typ)
{
try
{
if (leEnum == null) throw new ArgumentNullException("leEnum");
Type type = leEnum.GetType();
MemberInfo[] memInfo = type.GetMember(leEnum.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(EnumValue), false);
if (attrs != null && attrs.Length > 0)
return ((EnumValue)attrs[0]).Value;
}
return Decimal.MinValue;
}
catch (Exception)
{
throw;
}
}
Попробуйте!
Ответ 4
почему бы не сделать это в одном классе. Это немного больше работы в начале, но:
- намного проще расширить с помощью дополнительных значений
- может использовать вспомогательные функции для создания общих элементов (см.
Simple()
)
- более эффективен во время выполнения, потому что нет отражения для получения значений
- Связывание в Wpf проще
"{Binding namespace:DeviceType.All}"
и "{Binding SomeDeviceTypeProperty.Value}"
- недействительные значения aka
var invalid = (DeviceType)100;
пример кода
public class DeviceType
{
public static readonly DeviceType
Stb = new DeviceType("Stb", "Set Top Box", 19.95),
Panel = new DeviceType("Panel", 99),
Monitor = new DeviceType("Monitor", 19.95),
Cable = Simple("Cable"),
Connector = Simple("Connector"),
WirelessKeyboard = new DeviceType("WirelessKeyboard", "Wireless Keyboard", 20);
private static readonly IEnumerable<DeviceType> _all = typeof(DeviceType)
.GetFields(BindingFlags.Public | BindingFlags.Static).Select(f => (DeviceType)f.GetValue(null)).ToArray();
public static IEnumerable<DeviceType> All { get { return _all; } }
public static DeviceType Parse(string name)
{
foreach (var item in All)
{
if (item.Name == name)
return item;
}
throw new KeyNotFoundException(name);
}
private static DeviceType Simple(string name)
{
return new DeviceType(name, name, 9.95);
}
private DeviceType(string name, decimal value) : this(name, name, value) { }
private DeviceType(string name, string description, decimal value)
{
Name = name;
Description = description;
Value = value;
}
public string Name { get; private set; }
public string Description { get; private set; }
public decimal Value { get; private set; }
public override string ToString()
{
return Name;
}
}