Verifique se a propriedade possui atributo

158

Dada uma propriedade em uma classe, com atributos - qual é a maneira mais rápida de determinar se ela contém um determinado atributo? Por exemplo:

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

Qual é o método mais rápido para determinar que, por exemplo, ele tenha o atributo "IsIdentity"?

Otávio Décio
fonte

Respostas:

279

Não há uma maneira rápida de recuperar atributos. Mas o código deve ficar assim (crédito para Aaronaught ):

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

Se você precisar recuperar as propriedades do atributo,

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
}
Hans Passant
fonte
63
Se você só precisa verificar a existência do atributo e não recuperar nenhuma informação dele, o uso Attribute.IsDefinedeliminará uma linha de código e as feias matrizes / conversão.
Aaronaught 12/01
4
Algo que acabei de encontrar com isso é que alguns atributos têm um tipo diferente do nome do atributo. Por exemplo, "notmapped" em System.ComponentModel.DataAnnotations.Schema é usada como [NotMapped]na classe, mas para detectá-lo você tem que usarAttribute.IsDefined(pi, typeof(NotMappedAttribute))
Qjimbo
2
Talvez seja mais fácil usar a sobrecarga genérica:IsIdentity[] attr = pi.GetCustomAttributes<IsIdentity>(false);
Mojtaba
Os atributos @Qjimbo (ou provavelmente alguém que esteja lendo) geralmente são usados ​​sem a parte "Atributo" do nome, mas podem ser. Uma convenção permite que você a exclua, portanto, normalmente, o tipo real tem Atributo no final de seu nome, mas simplesmente não é usado.
Jim Wolff
44

Se você estiver usando o .NET 3.5, tente com as árvores de Expressão. É mais seguro que a reflexão:

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;
    }
}
Darin Dimitrov
fonte
7
Para sua informação, uma pergunta foi feita sobre sua resposta. stackoverflow.com/questions/4158996/…
Greg
12

Você pode usar um método comum (genérico) para ler o atributo em um determinado 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
fonte
7

Para atualizar e / ou aprimorar a resposta do @Hans Passant, eu separaria a recuperação da propriedade em um método de extensão. Isso tem o benefício adicional de remover a string mágica desagradável no método 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();
        }
    }
}

Seu teste é então reduzido para duas linhas

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

Se você está tentando fazer isso em um PCL da Biblioteca de Classes Portátil (como eu), aqui está como você pode fazê-lo :)

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

Você pode verificar o número de propriedades que possuem essa propriedade especial, se necessário.

Possui AlTaiar
fonte
7

Agora isso pode ser feito sem árvores de expressão e métodos de extensão de uma maneira segura com o novo recurso C # nameof()como este:

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

nameof () foi introduzido em C # 6

Jim Wolff
fonte
6

Você pode usar o método Attribute.IsDefined

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

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

Você pode fornecer a propriedade que procura especificamente ou pode percorrer todos eles usando a reflexão, algo como:

PropertyInfo[] props = typeof(YourClass).GetProperties();
Francis Musignac
fonte
Isso não compila. Você não pode usar [] em torno de YourProperty ou YourAttribute
rola
Todas as respostas anteriores usaram suposições sobre nomes de classe, propriedade e atributo que eu segui.
Francis Musignac 12/09
Aparece corrigido agora.
rola
2

Esta é uma pergunta bastante antiga, mas eu usei

Meu método tem esse parâmetro, mas pode ser construído:

Expression<Func<TModel, TValue>> expression

Então, no método, isso:

System.Linq.Expressions.MemberExpression memberExpression 
       = expression.Body as System.Linq.Expressions.MemberExpression;
Boolean hasIdentityAttr = System.Attribute
       .IsDefined(memberExpression.Member, typeof(IsIdentity));
Mark Schultheiss
fonte