【问题标题】:Sorting enum by a custom attribute按自定义属性对枚举进行排序
【发布时间】:2020-07-20 07:15:54
【问题描述】:

我找到了一种扩展方法,可以使用存储在自定义属性中的值对枚举进行排序。

public static class EnumExtenstions
{
    public static IEnumerable<TEnum> EnumGetOrderedValues<TEnum>(this Type enumType)
    {
        var fields = enumType.GetFields(BindingFlags.Public | BindingFlags.Static);
        var orderedValues = new List<Tuple<int, TEnum>>();
        foreach (var field in fields)
        {
            var orderAtt = field.GetCustomAttributes(typeof(EnumOrderAttribute), false).SingleOrDefault() as EnumOrderAttribute;
            if (orderAtt != null)
            {
                orderedValues.Add(new Tuple<int, TEnum>(orderAtt.Order, (TEnum)field.GetValue(null)));
            }
        }

        return orderedValues.OrderBy(x=>x.Item1).Select(x=>x.Item2).ToList();
    }
}

我想改变它,所以我将 EnumOrderAttribute 作为参数传入,并在每次迭代时计算出它的值。 EnumOrderAttribute 在不同的项目中声明。

我怎样才能改变这个方法来完成这个?

【问题讨论】:

  • 谢谢 Aybe,是的,这是我正在考虑更改的第二个答案
  • 当您说“更改此内容,以便我将 EnumOrderAttribute 作为参数传递”时,您是否尝试传递属性类型并相应地进行排序?还是这真的是重复的?
  • 嗨 Oguz,是的,传递属性类型并相应地进行排序
  • 您的意思是,就像您可以将任何属性类型作为通用参数或方法参数传递一样,(而不是将其硬编码为 EnumOrderAttribute 类型)?

标签: c# enums extension-methods custom-attributes


【解决方案1】:

我将尝试根据我对问题的理解提供解决方案。

据我了解,OP 希望为所有传入属性类型的枚举类型提供通用值排序方法,并找到一种使用此属性获取比较值的方法。

这是我的想法:

假设我们有一个属性,我们用它来装饰我们的枚举值以进行排序:

[AttributeUsage(AttributeTargets.Field)]
public class MyEnumOrderAttribute : Attribute
{
    public MyEnumOrderAttribute(int order)
    {
        Order = order;
    }

    public int Order { get; set; }
}

让我们有一个我们的枚举,其值使用我们的属性进行修饰:

public enum MyEnum
{
    [MyEnumOrder(2)]
    MyVal0 = 0,
    [MyEnumOrder(1)]
    MyVal1,
    [MyEnumOrder(3)]
    MyVal2,
    [MyEnumOrder(0)]
    MyVal3
}

这个枚举和属性可以是我们的,也可以来自第三方程序集,我们希望我们的方法使用这个属性对值进行排序,所以,我们需要传递属性的类型(这已经是一个通用参数),我们需要传递一个函数从属性实例中获取比较值。

因此;这是我们扩展方法的新实现:(稍后会对此作为扩展方法进行一些说明)

public static IEnumerable<TEnum> EnumGetOrderedValues<TEnum, TAttribute>
    (this Type enumType, Func<TAttribute, IComparable> valueExtractor)
    where TAttribute : Attribute
{
    var fields = typeof(TEnum).GetFields(BindingFlags.Public | BindingFlags.Static);
    var orderedValues = new List<Tuple<IComparable, TEnum>>();
    foreach (var field in fields)
    {
        var orderAtt = field.GetCustomAttributes(typeof(TAttribute), false).SingleOrDefault() as TAttribute;
        if (orderAtt != null)
        {
            orderedValues.Add(new Tuple<IComparable, TEnum>(valueExtractor(orderAtt), (TEnum)field.GetValue(null)));
        }
    }

    return orderedValues.OrderBy(x => x.Item1).Select(x => x.Item2).ToList();
}

如您所见,此方法现在适用于任何属性类型以及用于比较的任何值类型(int、string、任何 IComparable)

下面是如何使用它:

var orderedValues = typeof(MyEnum).EnumGetOrderedValues<MyEnum, MyEnumOrderAttribute>(x => x.Order);

请不要将x =&gt; x.Order 作为函数传递来获取属性值进行比较。我们可以在这里传递任何实现。

有序值的输出是:

    foreach (MyEnum orderedValue in orderedValues)
    {
        Console.WriteLine(orderedValue);
    }

MyVal3
MyVal1
MyVal0
MyVal2

编辑:注意该方法是扩展方法。

该方法是Type的扩展,我认为没有必要。

目前,我们这样称呼这个方法:

typeof(MyEnum).EnumGetOrderedValues<MyEnum, MyEnumOrderAttribute>(x => x.Order);

扩展方法已经在其第一个泛型参数中采用了枚举类型,并且可以在任何类型(typeof(string)?)上调用此方法,这可能会造成混淆。

为什么不把它作为一个静态方法呢?如果可以通过扩展在枚举本身上调用此方法,例如MyEnum.EnumGetOrderedValues,那将是有意义的。但既然不可能,我们可以直接改成静态方法:

public static IEnumerable<TEnum> EnumGetOrderedValues<TEnum, TAttribute>
    (Func<TAttribute, IComparable> valueExtractor)
    where TAttribute : Attribute

并使用typeof(TEnum)获取枚举类型

希望对你有帮助

【讨论】:

  • 不客气。待在家里,注意安全。保重。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-09
  • 2011-02-16
相关资源
最近更新 更多