遍历枚举元素(不只是名称,而不仅仅是值)

Iterate over Enum elements (Not just names and not just values)

我有枚举元素和描述属性的枚举

    public enum CourseGrades
    {
        [Description("A")]
        A = 11,
        [Description("A-")]
        AMinus = 10,
        [Description("B+")]
        BPlus = 9,
        [Description("B")]
        B = 8,
        [Description("B-")]
        BMinus = 7,
        [Description("C+")]
        CPlus = 6,
        [Description("C")]
        C = 5,
        [Description("C-")]
        CMinus = 4,
        [Description("D+")]
        DPlus = 3,
        [Description("D")]
        D = 2,
        [Description("D-")]
        DMinus = 1,
        [Description("E")]
        E = 0,
        [Description("Audit")]
        AU = -1,
        [Description("Incomplete")]
        IC = -2,
        [Description("Withdrawl")]
        WD = -3,
        [Description("Not Applicable")]
        NA = -4
    }

我的问题是我正在尝试建立一个描述列表,但我对如何做到这一点感到困惑。我看到的所有答案都说使用 Enum.GetNames()Enum.GetValues() 我知道该怎么做。

这两个的问题是它们 return 一个字符串数组,它丢失了关于它来自哪里的所有上下文,所以我无法按照它来获取特定枚举值的描述我想要的。

我现在获取描述的方法是调用 CourseGrades.A.GetDescription(),因为我有一个扩展方法可以处理获取描述属性 view code

我一直希望做这样的事情

var elements = System.Enum.GetElements(CourseGrades);
var dict = new Dictionary<CourseGrades, string>();

foreach (var element in elements) {
    dict.Add(element, element.GetDescription());
}

但我开始认为这样的事情是不可能做到的。

我一直在努力避免暴力破解

private Dictionary<CourseGrades, string> _courseGradesWithCaption = null;
public Dictionary < CourseGrades, string > CourseGradesWithCaptions
{
    get
    {
        if ( _courseGradesWithCaption == null )
            _courseGradesWithCaption = new Dictionary < CourseGrades, string > ()
            {
                { CourseGrades.A, CourseGrades.A.GetDescription () }
                , { CourseGrades.AMinus, CourseGrades.AMinus.GetDescription () }
                , { CourseGrades.BPlus, CourseGrades.BPlus.GetDescription () }
                // ... snip ...
            };
        return _courseGradesWithCaption;
    }
}

我以为我借用了上面链接的扩展方法如何通过枚举

public static class EnumerationCaptionsBuilder
{
    public static Dictionary < T, string > BuildCaptions<T> ( T e ) where T : IConvertible
    {
        if (!(e is System.Enum)) throw new ArgumentException("e is expected to be an enumeration.");

        var type = e.GetType ();
        var values = System.Enum.GetValues ( type );
        var dict = new Dictionary<T, string> ();


        foreach ( var val in values )
        {
            if ( (int) val != e.ToInt32 ( CultureInfo.InvariantCulture ) ) continue;

            var enumElement = type.GetMember ( type.GetEnumName ( val ) ?? throw new InvalidOperationException ());

            dict.Add(enumElement, enumElement.GetDescription());
        }
    }
}

但那时我才知道 type.GetMember return 是一个 MemberInfo 对象,这不是我要找的对象。

有没有办法做我想做的事情,或者我只是在树上狂吠?

我不完全明白为什么你的扩展不适合你(如果你在 Enum 上有它,你的 "requested" 代码应该可以工作)。

下面是我的做法:

public static class EnumerationExtensions
{
    public static string GetDescription(this Enum value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());

        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes( typeof(DescriptionAttribute), false);

        return attributes.Length > 0 ? attributes[0].Description : value.ToString();
    }
    public static IEnumerable<TEnum> GetEnumCollection<TEnum>(bool skipFirst = false)
    {
        var result = new List<TEnum>();
        var enums = Enum.GetValues(typeof(TEnum));
        for (int i = 0; i < enums.Length; i++)
        {
            if (skipFirst && i == 0) continue; //Some enums use "Invalid" or "Default" as first value
            TEnum e = (TEnum)enums.GetValue(i);
            result.Add(e);
        }
        return result;
    }
}

此外,需要考虑的一件事是您为什么需要它。如果这是为了显示目的,也许 IValueConverter 是您应该做的。这允许您拥有枚举类型的集合,然后在 IValueConverter 对象接收枚举然后 returns .Description 扩展调用时非常容易地显示描述。

我提供的示例有一个 "skipFirst" 默认为 false,因为当我进行枚举时,我经常先放置一个无效的状态(例如 Default、Undefined 等)以确保默认状态无效(我想要以确保已设置某些内容而不是使用默认值)。

编辑 添加我使用过的 IValueConverter。

public class EnumerationToDescriptionConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var casted = value as Enum;
        return casted?.GetDescription();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        //we have no need to get from visual descirption to the enumeration at this time.
        throw new NotImplementedException();
    }
}

然后你只需要将你的转换器实例化为一个带有键的资源并引用它。

<converters:EnumerationToDescriptionConverter x:Key="EnumerationToDescriptionConverter" />

<TextBlock Text="{Binding Converter={StaticResource EnumerationToDescriptionConverter}}"/>

在我的例子中,TextBlock 位于用于 ItemsControl 的 DataTemplate 中,该 ItemsControl 绑定到通过上面发布的其他扩展方法检索的枚举集合。