Como obter uma lista de propriedades com um determinado atributo?

210

Eu tenho um tipo, te eu gostaria de obter uma lista das propriedades públicas que tenham o atributo MyAttribute. O atributo é marcado com AllowMultiple = false, assim:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]

Atualmente, o que tenho é isso, mas estou pensando que há uma maneira melhor:

foreach (PropertyInfo prop in t.GetProperties())
{
    object[] attributes = prop.GetCustomAttributes(typeof(MyAttribute), true);
    if (attributes.Length == 1)
    {
         //Property with my custom attribute
    }
}

Como posso melhorar isso? Peço desculpas se esta é uma duplicata, há uma tonelada de tópicos de reflexão por aí ... parece que é um assunto bastante quente.

Wesville
fonte
Não. Você precisa de um PropertyInfo antes de descobrir se a propriedade possui um atributo.
Hans Passant

Respostas:

391
var props = t.GetProperties().Where(
                prop => Attribute.IsDefined(prop, typeof(MyAttribute)));

Isso evita a necessidade de materializar quaisquer instâncias de atributo (ou seja, é mais barato que GetCustomAttribute[s]().

Marc Gravell
fonte
1
Boa sugestão. No entanto, vou precisar da instância do atributo, mas eu gosto.
wsanville
1
Eu estava apenas procurando uma maneira de verificar a existência de um atributo sem o efeito colateral que a propriedade get é chamada. Obrigado Marc, funciona!
Örjan Jämte
1
@ ÖrjanJämte a propriedade getnão é chamada mesmo ao usar GetCustomAttributes; no entanto, o atributo é instanciado , o que não é gratuito. Se você não precisar verificar valores específicos do atributo, IsDefinedé mais barato. E em 4,5, existem maneiras de verificar os dados instanciação sem realmente criar quaisquer instâncias de atributos (embora este seja destinado para cenários muito específicos apenas)
Marc Gravell
2
@bjhuffine msdn.microsoft.com/en-us/library/…
Marc Gravell
2
para dotnet core: var props = t.GetProperties (). Where (e => e.IsDefined (typeof (MyAttribute)));
Rtype
45

A solução que eu mais uso é baseada na resposta de Tomas Petricek. Normalmente, eu quero fazer algo com tanto o atributo e propriedade.

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};
Wesville
fonte
+1 - "Normalmente, eu quero fazer algo com o atributo e a propriedade" é o que eu estava procurando - muito obrigado por postar sua resposta!
Yawar Murtaza
34

Até onde eu sei, não há maneira melhor de trabalhar com a biblioteca Reflection de maneira mais inteligente. No entanto, você pode usar o LINQ para tornar o código um pouco melhor:

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'

Acredito que isso ajude você a estruturar o código de maneira mais legível.

Tomas Petricek
fonte
13

Sempre há LINQ:

t.GetProperties().Where(
    p=>p.GetCustomAttributes(typeof(MyAttribute), true).Length != 0)
P Papai
fonte
6

Se você lida regularmente com Atributos em reflexão, é muito, muito prático definir alguns métodos de extensão. Você verá isso em muitos projetos por aí. Este aqui é um que eu geralmente tenho:

public static bool HasAttribute<T>(this ICustomAttributeProvider provider) where T : Attribute
{
  var atts = provider.GetCustomAttributes(typeof(T), true);
  return atts.Length > 0;
}

que você pode usar como typeof(Foo).HasAttribute<BarAttribute>();

Outros projetos (por exemplo, StructureMap) possuem classes ReflectionHelper de pleno direito que usam árvores de Expressão para ter uma sintaxe fina de identidade, por exemplo, PropertyInfos. O uso fica assim:

ReflectionHelper.GetProperty<Foo>(x => x.MyProperty).HasAttribute<BarAttribute>()
flq
fonte
2

Além das respostas anteriores: é melhor usar o método Any()do que verificar o tamanho da coleção:

propertiesWithMyAttribute = type.GetProperties()
  .Where(x => x.GetCustomAttributes(typeof(MyAttribute), true).Any());

O exemplo em dotnetfiddle: https://dotnetfiddle.net/96mKep

feeeper
fonte
@ cogumel0 Antes de tudo, com certeza .Any()não verifica o comprimento. Mas minha resposta não foi sobre propriedades encontradas com exatamente um atributo. Segundo, não tenho certeza de que você leu o código corretamente - .Anymétodo chamado no resultado do GetCustomAttrubutesmétodo. Portanto, o tipo do propertiesWithMyAttributeserá a coleção das propriedades. Confira o exemplo em dotnetfiddle (adiciono o link à resposta).
feeeper
1
Você pode substituir .Where por .Any, uma vez que .Any também permite lambdas.
PRMan