简体   繁体   中英

How to get a list of enum members that have been decorated with specific attribute

I want to return a list of enum members that have specific attribute. So for the code example I would like a list with enums SecondValue and ThirdValue, but not FirstValue or FourthValue because it doesn't have the attribute Container.

How could I do this?

PS: This was marked as duplicate before with link to this: Getting attributes of Enum's value

This is NOT the same question. I made more example code to make my question more clear. See the function definition and what it should return.

    [AttributeUsage(AttributeTargets.Field)]
    public sealed class ContainerAttribute : Attribute
    {
        public string Name { get; }

        public ContainerAttribute(string name)
        {
            Name = name;
        }
    }

    public enum MyEnum
    {
        [SomeOtherAttribute("attr")]
        FirstValue,

        [Container("name1")]
        SecondValue,

        [Container("name1")]
        ThirdValue,

        FourthValue
    }

    public List<MyEnum> GetEnumsWithAttribute(Attribute value) { }

    public void Main(String[] args) { 
       // someEnums should return list with content: [MyEnum.SecondValue, MyEnum.ThirdValue];
       var someEnums = GetEnumsWithAttribute(ContainerAttribute);
    }

Anytime you work with Attributes you will have to use Reflection. Mix in some Linq and you can inspect the fields of your enum to filter by attribute Type, then parse the results from field name.

Type ta = typeof(ContainerAttribute);
Type t = typeof(MyEnum);

var result = t.GetFields()
    .Where(a => a.CustomAttributes.Any(c => c.AttributeType == ta))
    .Select(a => (MyEnum)Enum.Parse(t, a.Name))
    .ToList();

Update

While it is not clear from your question if you require to further filter by Name , Guru's answer would imply that maybe you do, or will in the future. If so, you could edit my solution by changing the Where clause to accept a string that is the Name you want:

// method would take a string argument rather than hard-coding "name1"
.Where(a => a.CustomAttributes.Any(c => c.AttributeType == ta && c.ConstructorArguments.Any(ca => ca.Value.ToString() == "name1")))

You can use reflection for this:

public static List<MyEnum> GetEnumsWithAttribute(Attribute value)
    {
        return typeof(MyEnum)
           .GetMembers()
           .Where(m => m.MemberType == MemberTypes.Field)
           .Where(m => m.GetCustomAttributes().Any(a => a.Equals(value)))
           .Select(m => Enum.Parse<MyEnum>(m.Name))
           .ToList();
    }

This implementation relies on .Equals being overloaded in ContainerAttribute like this:

public override bool Equals(object? obj)
        {
            var other = obj as ContainerAttribute;
            if (other == null) return false;
            return this.Name == other.Name;
        }

If you don't want to override Equals you can change your method to be generic:

    public static List<MyEnum> GetEnumsWithAttribute<T>(T value, Func<T, T , bool> comparerFunc) where T: Attribute
    {
        return typeof(MyEnum)
           .GetMembers()
           .Where(m => m.MemberType == MemberTypes.Field)
           .Where(m => m.GetCustomAttributes().Any(a => a is T && comparerFunc((T)a, value)))
           .Select(m => Enum.Parse<MyEnum>(m.Name))
           .ToList();
    }

And usage:

var someEnums = GetEnumsWithAttribute(new ContainerAttribute("name1"), (a1, a2) => a1.Name == a2.Name);

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