wsanville
wsanville

Reputation: 37506

How to get a list of properties with a given attribute?

I have a type, t, and I would like to get a list of the public properties that have the attribute MyAttribute. The attribute is marked with AllowMultiple = false, like this:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]

Currently what I have is this, but I'm thinking there is a better way:

foreach (PropertyInfo prop in t.GetProperties())
{
    object[] attributes = prop.GetCustomAttributes(typeof(MyAttribute), true);
    if (attributes.Length == 1)
    {
         //Property with my custom attribute
    }
}

How can I improve this? My apologies if this is a duplicate, there are a ton of reflection threads out there...seems like it's quite a hot topic.

Upvotes: 258

Views: 141704

Answers (7)

RoJaIt
RoJaIt

Reputation: 461

I made my selfe an extension method for the class Type.

I used LINQ and the Tuples introduced in C# 7.0.

    public static class TypeExtensionMethods
    {
        public static List<(PropertyInfo Info, T Attribute)> GetPropertyWithAttribute<T>(this Type type) where T : Attribute
        {
#pragma warning disable CS8619 // Checked here: .Where(x => x.Value != default)

            return type.GetProperties()
                       .Select(x => (Info: x, Attribute: GetAttribute<T>(x)))
                       .Where(x => x.Attribute != default)
                       .ToList();

#pragma warning restore CS8619 // Checked here: .Where(x => x.Value != default) 
        }

        private static T? GetAttribute<T>(PropertyInfo info) where T : Attribute
        {
            return (T?)info.GetCustomAttributes(typeof(T), true)
                           .FirstOrDefault();
        }
    }

It can be used like this:

var props = typeof(DemoClass).GetPropertyWithAttribute<MyAttribute>();

foreach((PropertyInfo Info, MyAttribute Attribute) in props)
{
    Console.WriteLine($"Property {Info.Name}: {Attribute.DisplayName}");
}

The pragma could be removed, but I don't want any warnings...

Upvotes: 0

feeeper
feeeper

Reputation: 3017

In addition to previous answers: it's better to use method Any() instead of check length of the collection:

propertiesWithMyAttribute = type.GetProperties()
  .Where(x => x.GetCustomAttributes(typeof(MyAttribute), true).Any());

The example at dotnetfiddle: https://dotnetfiddle.net/96mKep

Upvotes: 4

Tomas Petricek
Tomas Petricek

Reputation: 243041

As far as I know, there isn't any better way in terms of working with Reflection library in a smarter way. However, you could use LINQ to make the code a bit nicer:

var props = from p in t.GetProperties()
            let attrs = p.GetCustomAttributes(typeof(MyAttribute), true)
            where attrs.Length != 0 select p;

// Do something with the properties in 'props'

I believe this helps you to structure the code in a more readable fashion.

Upvotes: 36

wsanville
wsanville

Reputation: 37506

The solution I end up using most is based off of Tomas Petricek's answer. I usually want to do something with both the attribute and property.

var props = from p in this.GetType().GetProperties()
            let attr = p.GetCustomAttributes(typeof(MyAttribute), true)
            where attr.Length == 1
            select new { Property = p, Attribute = attr.First() as MyAttribute};

Upvotes: 55

Marc Gravell
Marc Gravell

Reputation: 1062540

var props = t.GetProperties().Where(
                prop => Attribute.IsDefined(prop, typeof(MyAttribute)));

This avoids having to materialize any attribute instances (i.e. it is cheaper than GetCustomAttribute[s]().

Upvotes: 480

flq
flq

Reputation: 22829

If you deal regularly with Attributes in Reflection, it is very, very practical to define some extension methods. You will see that in many projects out there. This one here is one I often have:

public static bool HasAttribute<T>(this ICustomAttributeProvider provider) where T : Attribute
{
  var atts = provider.GetCustomAttributes(typeof(T), true);
  return atts.Length > 0;
}

which you can use like typeof(Foo).HasAttribute<BarAttribute>();

Other projects (e.g. StructureMap) have full-fledged ReflectionHelper classes that use Expression trees to have a fine syntax to identity e.g. PropertyInfos. Usage then looks like that:

ReflectionHelper.GetProperty<Foo>(x => x.MyProperty).HasAttribute<BarAttribute>()

Upvotes: 7

P Daddy
P Daddy

Reputation: 29527

There's always LINQ:

t.GetProperties().Where(
    p=>p.GetCustomAttributes(typeof(MyAttribute), true).Length != 0)

Upvotes: 12

Related Questions