252

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.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
wsanville
  • 37,158
  • 8
  • 76
  • 101
  • Nope. You need a PropertyInfo before you can find out if the property has an attribute. – Hans Passant Feb 17 '10 at 15:48
  • [This](https://stackoverflow.com/questions/69496590/how-to-read-custom-attribute-parameters-and-values-attached-to-a-class-or-method) might be also helpful. – Matt Oct 15 '21 at 07:39

7 Answers7

470
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]().

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    Good suggestion. I will however need the attribute instance, but I like it. – wsanville Feb 17 '10 at 16:26
  • 1
    I was just looking for a way to check the existence of an attribute without the side-effect that the property get is called. Thanks Marc, it work! – Örjan Jämte Nov 18 '13 at 14:29
  • 2
    @ÖrjanJämte the property `get` is not called even when using `GetCustomAttributes`; however, the attribute is *instantiated*, which isn't free. If you don't need to check specific values of the attribute, `IsDefined` is cheaper. And in 4.5, there are ways to check the instantiation data *without* actually creating any attribute instances (although this is *intended* for very specific scenarios only) – Marc Gravell Nov 18 '13 at 14:31
  • @Mark Gravell, "And in 4.5, there are ways to check the instantiation data without actually creating any attribute instances". What do you mean? – bjhuffine Nov 03 '14 at 19:29
  • 2
    @bjhuffine http://msdn.microsoft.com/en-us/library/system.reflection.memberinfo.getcustomattributesdata(v=vs.110).aspx – Marc Gravell Nov 03 '14 at 21:53
  • 2
    for dotnet core: var props = t.GetProperties().Where(e => e.IsDefined(typeof(MyAttribute))); – Rtype Aug 31 '17 at 00:43
  • I can 100% vouch for this method as being much more performant than a ".Where(p => p.GetCustomAttributes..." – Blackey Jan 07 '21 at 14:30
54

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};
wsanville
  • 37,158
  • 8
  • 76
  • 101
  • 1
    +1 - "I usually want to do something with both the attribute and property" is what i was looking for - many thanks for posting your answer! – Yawar Murtaza Nov 27 '17 at 09:19
36

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.

Black Frog
  • 11,595
  • 1
  • 35
  • 66
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
12

There's always LINQ:

t.GetProperties().Where(
    p=>p.GetCustomAttributes(typeof(MyAttribute), true).Length != 0)
P Daddy
  • 28,912
  • 9
  • 68
  • 92
6

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>()
flq
  • 22,247
  • 8
  • 55
  • 77
3

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

feeeper
  • 2,865
  • 4
  • 28
  • 42
  • @cogumel0 First of all, sure `.Any()` does not check the length. But my answer was not about found properties with exactly one attribute. Second, I'm not sure that you read the code correctly - `.Any` method called on the result of the `GetCustomAttrubutes` method. So the type of the `propertiesWithMyAttribute` will be the collection of the properties. Check out the example at dotnetfiddle (I add the link to the answer). – feeeper Sep 05 '18 at 10:04
  • 2
    You can replace .Where with .Any, since .Any also allows lambdas. – PRMan Jun 27 '19 at 20:24
0

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...

RoJaIt
  • 451
  • 3
  • 10