Проверьте, если свойство имеет атрибут

158

Учитывая свойство в классе с атрибутами - какой самый быстрый способ определить, содержит ли оно данный атрибут? Например:

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

Какой самый быстрый способ определить, например, что он имеет атрибут «IsIdentity»?

Otávio Décio
источник

Ответы:

280

Там нет быстрого способа получить атрибуты. Но код должен выглядеть следующим образом (благодарность Ааронау ):

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 (или, возможно, кто-то еще читает) Атрибуты обычно используются без части «Атрибут» их имени, но могут быть. Соглашение позволяет вам исключить его, поэтому обычно фактический тип имеет атрибут Attribute в конце своего имени, но просто не используется.
Джим Вольф
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
К вашему сведению, был задан вопрос о вашем ответе. stackoverflow.com/questions/4158996/...
Greg
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 Portable Class Library (как я), то вот как вы можете это сделать :)

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));
Марк Шультхайс
источник