Перевірте, чи властивість має атрибут


158

Дана властивість у класі з атрибутами - який найшвидший спосіб визначити, чи містить він заданий атрибут? Наприклад:

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

Який найшвидший метод визначити, що, наприклад, він має атрибут "IsIdentity"?

Відповіді:


279

Немає швидкого способу отримання атрибутів. Але код повинен виглядати приблизно так (кредит Aaronaught ):

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

Якщо вам потрібно відновити властивості атрибутів, тоді

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
}

63
Якщо вам потрібно лише перевірити наявність атрибута, а не отримувати з нього будь-яку інформацію, за допомогою Attribute.IsDefinedвилучіть один рядок коду та некрасиві масиви / кастинг.
Aaronaught

4
Щось я просто зіткнувся з цим, це те, що деякі атрибути мають інший тип їх атрибутів. Наприклад, "NotMapped" в System.ComponentModel.DataAnnotations.Schema використовується як [NotMapped]у класі, але для її виявлення вам доведеться скористатисяAttribute.IsDefined(pi, typeof(NotMappedAttribute))
Qjimbo

2
Можливо, буде легше використовувати загальне перевантаження:IsIdentity[] attr = pi.GetCustomAttributes<IsIdentity>(false);
Mojtaba

@Qjimbo (або, мабуть, хтось інший, що читає) Атрибути зазвичай використовуються без частини "Атрибута" їх імені, але можуть бути. Конвенція дозволяє виключити її, тому зазвичай фактичний тип має атрибут у кінці своєї назви, але він просто не використовується.
Джим Волф

44

Якщо ви використовуєте .NET 3.5, ви можете спробувати з деревами виразів. Це безпечніше, ніж рефлексія:

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

7
FYI було поставлено запитання про вашу відповідь. stackoverflow.com/questions/4158996/…
Грег

12

Ви можете використовувати загальний (загальний) метод для читання атрибутів для певного 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;
            }

7

Для оновлення та / або покращення відповіді від @Hans Passant я б розділив пошук властивості на метод розширення. Це має додаткову перевагу видалення бридкої магічної струни в методі 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();
        }
    }
}

Потім ваш тест зменшується до двох рядків

var property = PropertyHelper<MyClass>.GetProperty(x => x.MyProperty);
Attribute.IsDefined(property, typeof(MyPropertyAttribute));

7

Якщо ви намагаєтеся зробити це в PCL бібліотеки портативних класів (як я), то ось як це можна зробити :)

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

Потім ви можете перевірити кількість властивостей, які мають це особливе властивість, якщо вам потрібно.


7

Тепер це можна зробити без дерев виразів та методів розширення безпечним способом, використовуючи нову функцію C # nameof():

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

nameof () було введено в C # 6


6

Ви можете використовувати метод Attribute.IsDefined

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

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

Ви можете надати власність, яку ви спеціально шукаєте, або ви могли переглядати їх за допомогою відображення, наприклад:

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

Це не компілюється. Ви не можете використовувати [] навколо YourProperty або YourAttribute
згортається

Кожна попередня відповідь використовувала припущення щодо імен класів, властивостей та атрибутів, за якими я слідував.
Френсіс Музіньяк

З’являється виправлено зараз.
котиться

2

Це досить старе питання, але я використовував

У мого методу є цей параметр, але його можна побудувати:

Expression<Func<TModel, TValue>> expression

Тоді в методі це:

System.Linq.Expressions.MemberExpression memberExpression 
       = expression.Body as System.Linq.Expressions.MemberExpression;
Boolean hasIdentityAttr = System.Attribute
       .IsDefined(memberExpression.Member, typeof(IsIdentity));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.