【问题标题】:Multiple enum descriptions多个枚举描述
【发布时间】:2015-12-30 08:17:20
【问题描述】:

我已经定义了以下enum

public enum DeviceType
{
    [Description("Set Top Box")]
    Stb = 1,
    Panel = 2,
    Monitor = 3,
    [Description("Wireless Keyboard")]
    WirelessKeyboard = 4
}

我正在利用Description 属性来允许我提取出一个更易于用户阅读的枚举版本以显示在 UI 中。我使用以下代码获得描述:

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 属性,它会给我枚举的字符串值。

我现在想为每个枚举添加第二个/自定义属性(例如,称为“值”)。例如:

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 属性,或者最好单独创建新属性?

【问题讨论】:

  • 这是 XY 问题。你最终想达到什么目标?
  • 您能否举例说明包含 value 属性的结果应该如何? (不是 100% 清楚你到底想要什么)
  • 您可以扩展或创建一个新的,这不是什么大问题。但是我还要说,当您需要越来越多的枚举属性时,您也许应该考虑创建一个类。..
  • 我似乎真的希望DeviceType 成为class 而不仅仅是enum。类可以有多个属性。
  • 我知道这是一个例子,但为什么会有一个Value 属性,它采用double 的字符串表示?

标签: c# enums


【解决方案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 我确实喜欢使用扩展方法,非常好!对于没有描述但需要 Value 属性的 DeviceType.Panel 的情况,您有解决方案吗? (参见帕特里克的回答 cmets)

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

【讨论】:

  • 您应该真正派生现有的DescriptionAttribute,因为例如 WPF 在其转换器中使用它。
  • 它基本上变成了与你提供的答案相同的答案,我将删除这篇文章,因为它没有比你的更有价值,但考虑添加我列出的扩展方法给你因为您可以全面重复使用它
  • 同意这是一个不错的选择,并且投票赞成。为什么不把它留在那里,让这个问题保持原样?只是想。
  • @Aydin Adn 我非常喜欢使用扩展方法,非常好!对于没有描述但需要 Value 属性的 DeviceType.Panel 的情况,您有解决方案吗? (参见帕特里克的回答 cmets)
【解决方案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,
}

您检索属性的代码几乎保持不变,只需替换类型名称。

【讨论】:

  • 在没有描述但需要 Value 属性的情况下,这如何工作?参考我原来的帖子,DeviceType.Panel?
  • 如果你想拆分逻辑,你总是可以创建一个单独的枚举。只是不要从DescriptionAttribute 派生,而只是Attribute
  • 使用您的代码,我能够通过修改用于获取 Description 属性的原始代码来获取 Value 属性: var attributes = (DescriptionWithValueAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionWithValueAttribute), false );有一个单独的枚举意味着有这个代码的多个变体?
  • 确实,这不是很好,但如果它适合您的用例,则可以。
【解决方案3】:

为什么不在一节课上做呢。一开始还需要做更多的工作,但是:

  • 使用附加值更容易扩展
  • 可以使用辅助函数来创建通用元素(参见Simple()
  • 在运行时更高效,因为无需反射来获取值
  • 在 Wpf 中绑定更容易 "{Binding namespace:DeviceType.All}""{Binding SomeDeviceTypeProperty.Value}"
  • 没有无效值,即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;
    }
}

【讨论】:

    【解决方案4】:

    您要做的是:创建一个属性来更具体地描述枚举:您可以这样做:

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

    试一试!

    【讨论】:

    • 您应该真正派生现有的DescriptionAttribute,因为例如 WPF 在其转换器中使用它。
    • 没关系。如果 OP 出于某种原因使用它,最好保持原样。只是我的意见。
    猜你喜欢
    • 1970-01-01
    • 2012-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多