Подтвердить что ты не робот

Несколько описаний перечислений

Я определил следующее 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, или лучше всего создать новый атрибут отдельно?

4b9b3361

Ответ 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;
    }
}