219

Given a property in a class, with attributes - what is the fastest way to determine if it contains a given attribute? For example:

    [IsNotNullable]
    [IsPK]
    [IsIdentity]
    [SequenceNameAttribute("Id")]
    public Int32 Id
    {
        get
        {
            return _Id;
        }
        set
        {
            _Id = value;
        }
    }

What is the fastest method to determine that for example it has the "IsIdentity" attribute?

Otávio Décio
  • 73,752
  • 17
  • 161
  • 228

8 Answers8

350

There's no fast way to retrieve attributes. But code ought to look like this (credit to Aaronaught):

var t = typeof(YourClass);
var pi = t.GetProperty("Id");
var hasIsIdentity = Attribute.IsDefined(pi, typeof(IsIdentity));

If you need to retrieve attribute properties then

var t = typeof(YourClass);
var pi = t.GetProperty("Id");
var attr = (IsIdentity[])pi.GetCustomAttributes(typeof(IsIdentity), false);
if (attr.Length > 0) {
    // Use attr[0], you'll need foreach on attr if MultiUse is true
}
Rob
  • 26,989
  • 16
  • 82
  • 98
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 74
    If you only need to check for the existence of the attribute, and not retrieve any information from it, using `Attribute.IsDefined` will eliminate one line of code and the ugly arrays/casting. – Aaronaught Jan 12 '10 at 18:42
  • 5
    Something I just ran into with this is some attributes have a different type to their attribute name. For example "NotMapped" in System.ComponentModel.DataAnnotations.Schema is used as `[NotMapped]` in the class but to detect it you have to use `Attribute.IsDefined(pi, typeof(NotMappedAttribute))` – Qjimbo Jul 13 '18 at 21:41
  • 2
    Might be easier to use the generic overload: `IsIdentity[] attr = pi.GetCustomAttributes(false);` – Mojtaba Mar 28 '19 at 11:27
  • 1
    @Qjimbo (or probably someone else reading) Attributes are usually used without the "Attribute" part of their name, but can be. A convention allows you to exclude it, so usually the actual type does have Attribute at the end of its name, but is just not used. – Jim Wolff Mar 13 '20 at 20:29
50

If you are using .NET 3.5 you might try with Expression trees. It is safer than reflection:

class CustomAttribute : Attribute { }

class Program
{
    [Custom]
    public int Id { get; set; }

    static void Main()
    {
        Expression<Func<Program, int>> expression = p => p.Id;
        var memberExpression = (MemberExpression)expression.Body;
        bool hasCustomAttribute = memberExpression
            .Member
            .GetCustomAttributes(typeof(CustomAttribute), false).Length > 0;
    }
}

This is safer because it is tied to the property Id itself. If the property's name changes, this fails in compile time.

Whereas reflection is tied to a string "Id" which will not fail to compile if the property's name changes, and will only fail when executing that code.
Modern versions of C# can avoid this by using nameof(MyClass.Id) for reflection, instead of "Id".

ANeves
  • 6,219
  • 3
  • 39
  • 63
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 8
    FYI a question has been asked about your answer. http://stackoverflow.com/questions/4158996/why-are-expression-trees-safer-than-reflection – Greg Nov 11 '10 at 20:35
23

This can now be done without expression trees and extension methods in a type safe manner with the new C# feature nameof() like this:

Attribute.IsDefined(typeof(YourClass).GetProperty(nameof(YourClass.Id)), typeof(IsIdentity));

nameof() was introduced in C# 6

Jim Wolff
  • 5,052
  • 5
  • 34
  • 44
15

You can use a common (generic) method to read attribute over a given MemberInfo

public static bool TryGetAttribute<T>(MemberInfo memberInfo, out T customAttribute) where T: Attribute {
                var attributes = memberInfo.GetCustomAttributes(typeof(T), false).FirstOrDefault();
                if (attributes == null) {
                    customAttribute = null;
                    return false;
                }
                customAttribute = (T)attributes;
                return true;
            }
Manish Basantani
  • 16,931
  • 22
  • 71
  • 103
15

You can use the Attribute.IsDefined method

https://msdn.microsoft.com/en-us/library/system.attribute.isdefined(v=vs.110).aspx

if(Attribute.IsDefined(YourProperty,typeof(YourAttribute)))
{
    //Conditional execution...
}

You could provide the property you're specifically looking for or you could iterate through all of them using reflection, something like:

PropertyInfo[] props = typeof(YourClass).GetProperties();
8

To update and/or enhance the answer by @Hans Passant I would separate the retrieval of the property into an extension method. This has the added benefit of removing the nasty magic string in the method GetProperty()

public static class PropertyHelper<T>
{
    public static PropertyInfo GetProperty<TValue>(
        Expression<Func<T, TValue>> selector)
    {
        Expression body = selector;
        if (body is LambdaExpression)
        {
            body = ((LambdaExpression)body).Body;
        }
        switch (body.NodeType)
        {
            case ExpressionType.MemberAccess:
                return (PropertyInfo)((MemberExpression)body).Member;
            default:
                throw new InvalidOperationException();
        }
    }
}

Your test is then reduced to two lines

var property = PropertyHelper<MyClass>.GetProperty(x => x.MyProperty);
Attribute.IsDefined(property, typeof(MyPropertyAttribute));
Seb
  • 179
  • 2
  • 5
8

If you are trying to do that in a Portable Class Library PCL (like me), then here is how you can do it :)

public class Foo
{
   public string A {get;set;}

   [Special]
   public string B {get;set;}   
}

var type = typeof(Foo);

var specialProperties = type.GetRuntimeProperties()
     .Where(pi => pi.PropertyType == typeof (string) 
      && pi.GetCustomAttributes<Special>(true).Any());

You can then check on the number of properties that have this special property if you need to.

Has AlTaiar
  • 4,052
  • 2
  • 36
  • 37
2

This is a pretty old question but I used

My method has this parameter but it could be built:

Expression<Func<TModel, TValue>> expression

Then in the method this:

System.Linq.Expressions.MemberExpression memberExpression 
       = expression.Body as System.Linq.Expressions.MemberExpression;
Boolean hasIdentityAttr = System.Attribute
       .IsDefined(memberExpression.Member, typeof(IsIdentity));
Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100