简体   繁体   中英

Sorting enum by a custom attribute

I have found an extension method to order an enum with a value stored in a custom attribute.

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

I would like to alter this so I am passing in the EnumOrderAttribute as an argument and figure out its value per iteration. The EnumOrderAttribute is declared in a different project.

How can I alter this method to accomplish this?

I will try to provide a solution as to my understanding of the problem.

As I understand it, the OP wants to have a generic value ordering method for all enum types passing in the attribute type and by finding a way to get the comparison values using this attribute.

Here is what I have in mind:

Let's assume we have an attribute with which we decorate our enum values for ordering:

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

    public int Order { get; set; }
}

And let's have an enum of ours whose values are decorated using our attribute:

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

This enum and attribute can be ours or can be from a third party assembly, and we want our method to order the values by using this attribute, so, we need to pass the type of attribute (which is already a generic parameter), and we need to pass a function to get the comparison value from the attribute instance.

Therefore; here is the new implementation of our extension method: (Some notes on this being an extension method later)

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

As you can see, this method now works with any attribute type with any value type used for comparison (int, string, any IComparable)

And here is how to use it:

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

Please not the passing of x => x.Order as a function to get the attribute value for comparison. We can pass any implementation here.

And the output for the ordered values is:

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

MyVal3
MyVal1
MyVal0
MyVal2

EDIT: Note on the method being an extension method.

The method is an extension to Type , which I think is not necessary.

Currently, we call this method like this:

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

The extension method already takes the enum type in its first generic parameter, and calling this method on any type (typeof(string)?) is possible, which can be confusing.

Why not have it as a static method? If it had been possible by being an extension to call this method on an enum itself, like MyEnum.EnumGetOrderedValues , it would made sense. But since it is not possible, we can just change it to a static method:

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

and get the enum type inside using typeof(TEnum)

Hope this helps

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM