【问题标题】:Get a List<string> of my enum attributes with a generic method使用通用方法获取我的枚举属性的 List<string>
【发布时间】:2012-08-14 21:24:09
【问题描述】:

一开始,我们有这个基本的枚举。

public enum E_Levels {

    [ValueOfEnum("Low level")]
    LOW,

    [ValueOfEnum("Normal level")]
    NORMAL,

    [ValueOfEnum("High level")]
    HIGH
}

我想得到一个List&lt;string&gt; 无论枚举是什么。像Extensions.GetValuesOfEnum&lt;E_Levels&gt;() 这样的东西可以返回一个List&lt;string&gt;,其中包含“低级别”、“正常级别”和“高级别”。

StackOF 帮我获得了一个值属性:

public static class Extensions {

    public static string ToValueOfEnum(this Enum value) {

        FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
        ValueOfEnum[] attribs = fieldInfo.GetCustomAttributes(typeof(ValueOfEnum), false) as ValueOfEnum[];
        return attribs.Length > 0 ? attribs[0].value : null;
    }
}

无论枚举是什么,我都可以调用此方法:E_Levels.LOW.ToValueOfEnum()

此外,StackOF 帮助我获得了一个特定枚举的List&lt;string&gt;。 我在控制器中制作了这个方法:

private List<string> GetLevels() {

List<string> levelsToReturn = new List<string>();
var levels = Enum.GetValues(typeof(E_Levels)).Cast<E_Levels>();
foreach(E_Levels l in levels) 
    levelsToReturn.Add(l.ToValueOfEnum());

return levelsToReturn;
}

但是这种方式需要我为每个枚举重写相同的方法。
所以我尝试将这个通用方法添加到我的类扩展中:

public static class Extensions {

    public static string ToValueOfEnum(this Enum value) {...}

    public static List<string> GetValuesOf<T>() {

        List<string> levelsToReturn = new List<string>();
        var levels = Enum.GetValues(typeof(T)).Cast<T>();
        foreach(T l in levels) 
        levelsToReturn.Add(l.ToValueOfEnum());

        return levelsToReturn;
    }
}

但在我的 foreach 中,.ToValueOfEnum() 是一种未知方法。

所以我遇到了麻烦,我希望我能找到一种方法,不要一次又一次地为每个枚举重写相同的方法......

【问题讨论】:

    标签: c# generics enums custom-attributes


    【解决方案1】:

    让我们尽量保持这个更通用的目的。

    我有一个extension method 可以从枚举值中获取属性。这将使您能够快速访问属性。

    public static class EnumExtensions
    {
        public static TAttribute GetAttribute<TAttribute>(this Enum value)
            where TAttribute : Attribute
        {
            var type = value.GetType();
            var name = Enum.GetName(type, value);
            return type.GetField(name)
                .GetCustomAttributes(false)
                .OfType<TAttribute>()
                .SingleOrDefault();
        }
    }
    

    使用它,您可以创建一些查询来获得您想要的。

    var valuesOfLevels =
        Enum.GetValues(typeof(E_Levels)).Cast<E_Levels>()
            .Select(level => level.GetAttribute<ValueOfEnumAttribute>().Value);
    

    所以你的GetValuesOf() 方法(恕我直言,这不是一个很好的特殊方法)可以这样写:

    public static List<string> GetValuesOf<TEnum>()
        where TEnum : struct // can't constrain to enums so closest thing
    {
        return Enum.GetValues(typeof(TEnum)).Cast<Enum>()
                   .Select(val => val.GetAttribute<ValueOfEnumAttribute>().Value)
                   .ToList();
    }
    

    现在你可以像这样调用该方法:

    var levelValues = GetValueOf<E_Levels>();
    // levelValues = { "Low level", "Normal level", "High level" }
    

    【讨论】:

    • 感谢您的回答杰夫,这就是我要找的。 Select() 取代了我的 foreach,而你的 GetAttribute&lt;&gt;() 方法是锦上添花。我将把你的GetCustomAttributes() 从@GSerjo 切换到Attribute.GetCustomAttribute()。你是对的:GetValuesOf 不是一个相关的名字......
    • 感谢您的解决方案,它节省了我的时间。
    【解决方案2】:

    .Net 已经有相同的属性Description,所以你可以用这个来代替ValueOfEnum

    dynamictype 上的扩展名和以下示例怎么样

    [TestFixture]
    public sealed class ForTest
    {
        [Test]
        public void Test()
        {
            var values = typeof(Levels).ToValues();
            values.ForEach(Console.WriteLine);
        }
    }
    
    public static class TypeExtensions
    {
        public static List<string> ToValues(this Type value)
        {
            var result = new List<string>();
            var values = ToConcreteValues(value);
            foreach (dynamic item in values)
            {
                Description attribute = GetAttribute<Description>(item);
                result.Add(attribute.Description);
            }
            return result;
        }
    
        private static dynamic ToConcreteValues(Type enumType)
        {
            Array values = Enum.GetValues(enumType);
            Type list = typeof (List<>);
            Type resultType = list.MakeGenericType(enumType);
            dynamic result = Activator.CreateInstance(resultType);
            foreach (object value in values)
            {
                dynamic concreteValue = Enum.Parse(enumType, value.ToString());
                result.Add(concreteValue);
            }
            return result;
        }
    
        private static TAttribute GetAttribute<TAttribute>(dynamic value)
            where TAttribute : Attribute
        {
            Type type = value.GetType();
            FieldInfo fieldInfo = type.GetField(Enum.GetName(type, value));
            return (TAttribute) Attribute.GetCustomAttribute(fieldInfo, typeof (TAttribute));
        }
    }
    
    public enum Levels
    {
        [Description("Low level")]LOW,
        [Description("Normal level")] NORMAL,
        [Description("High level")] HIGH
    }
    

    结果输出:

    Low level
    Normal level
    High level
    

    【讨论】:

    • 我忽略了 Description 已经在 .NET 中,我认为人们将它用作自定义属性。关于你的方法,我觉得很有趣。我密封了我的自定义属性。但是使用 dynamic 类型有什么好处呢?我听说dynamic 不是安全类型,因为编译器不会检查您调用的方法是否存在。
    • 只有一个,你可以在任何枚举类型上创建扩展,是的,动态只能在运行时检查。
    • 嗯...但是@JeffMercado 提出了一个没有dynamic 的类似方法,对吗? (你只是得到你的List&lt;string&gt; 像这样:GetValueOf&lt;E_Levels&gt;())或者你的解决方案有另一个收获?
    • 几乎,所有解决方案都很丑陋,所有版本只能在运行时检查。通过 GetValueOf 的版本将快速运行,然后通过 dinamic
    【解决方案3】:

    您可以尝试强制转换(Enum)(object)l,将ToValueOfEnum 更改为object,或者只是内联方法:

    public static List<string> GetValuesOf<T>()
    {
    
        List<string> levelsToReturn = new List<string>();
        var levels = Enum.GetValues(typeof(T)).Cast<T>();
        foreach (T value in levels)
        {
            FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
            ValueOfEnum[] attribs = fieldInfo.GetCustomAttributes(typeof(ValueOfEnum), false) as ValueOfEnum[];
            levelsToReturn.Add(attribs.Length > 0 ? attribs[0].value : null);
        }
    
        return levelsToReturn;
    }
    

    这是使用强制转换方法的单行解决方案:

    return new List<string>(Enum.GetValues(typeof(T)).Cast<Enum>().Select(x => x.ToValueOfEnum()));
    

    如果您不清楚为什么 T 没有像 E_Levels 那样被识别为 Enum,那是因为您没有指定 T : enum。不幸的是,你不能在 C# 中指定它(即使 CLR 支持它),所以其他方法,如运行时检查/假设(比如我在这里建议的)或编译后代码修改(例如 unconstrained-melody)有完成。

    【讨论】:

    • 我猜单行解决方案比使用 foreach 显式循环更干净。我现在明白为什么我不能使用我的枚举方法了……
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-07
    • 2011-02-16
    • 1970-01-01
    • 2022-06-18
    • 2010-12-20
    相关资源
    最近更新 更多