¿Cómo obtener una lista de propiedades con un atributo dado?

210

Tengo un tipo, ty me gustaría obtener una lista de las propiedades públicas que tienen el atributo MyAttribute. El atributo está marcado con AllowMultiple = false, así:

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

Actualmente lo que tengo es esto, pero creo que hay una mejor manera:

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

¿Cómo puedo mejorar esto? Mis disculpas si esto es un duplicado, hay un montón de hilos de reflexión por ahí ... parece que es un tema bastante candente.

wsanville
fuente
No Necesita un PropertyInfo antes de poder averiguar si la propiedad tiene un atributo.
Hans Passant

Respuestas:

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

Esto evita tener que materializar cualquier instancia de atributo (es decir, es más barato que GetCustomAttribute[s]().

Marc Gravell
fuente
1
Buena sugerencia. Sin embargo, necesitaré la instancia de atributo, pero me gusta.
wsanville
1
Estaba buscando una forma de verificar la existencia de un atributo sin el efecto secundario que se llama a la propiedad get. Gracias Marc, funciona!
Örjan Jämte
1
@ ÖrjanJämte la propiedad getno se llama incluso cuando se usa GetCustomAttributes; sin embargo, el atributo es instanciado , lo que no es gratuito. Si no necesita verificar valores específicos del atributo, IsDefinedes más barato. Y en 4.5, hay maneras de comprobar los datos de creación de instancias y sin crear realmente cualquier instancia de atributos (aunque esto está destinado para escenarios muy específicos)
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

La solución que más utilizo se basa en la respuesta de Tomas Petricek. Por lo general quiero hacer algo con tanto el atributo y la propiedad.

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};
wsanville
fuente
+1 - "Normalmente quiero hacer algo con el atributo y la propiedad" es lo que estaba buscando. ¡Muchas gracias por publicar su respuesta!
Yawar Murtaza
34

Hasta donde sé, no hay mejor manera de trabajar con la biblioteca Reflection de una manera más inteligente. Sin embargo, podría usar LINQ para que el código sea un poco más agradable:

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'

Creo que esto te ayuda a estructurar el código de una manera más legible.

Tomás Petricek
fuente
13

Siempre hay LINQ:

t.GetProperties().Where(
    p=>p.GetCustomAttributes(typeof(MyAttribute), true).Length != 0)
Papi
fuente
6

Si trata regularmente con Atributos en Reflection, es muy, muy práctico definir algunos métodos de extensión. Verá eso en muchos proyectos por ahí. Este de aquí es uno que a menudo tengo:

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

que puedes usar como typeof(Foo).HasAttribute<BarAttribute>();

Otros proyectos (p. Ej., StructureMap) tienen clases ReflectionHelper completas que utilizan árboles de expresión para tener una sintaxis precisa para la identidad, p. Ej. PropertyInfos. El uso entonces se ve así:

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

Además de las respuestas anteriores: es mejor usar el método en Any()lugar de verificar la longitud de la colección:

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

El ejemplo en dotnetfiddle: https://dotnetfiddle.net/96mKep

más débil
fuente
@ cogumel0 En primer lugar, seguro .Any()no verifica la longitud. Pero mi respuesta no fue sobre propiedades encontradas con exactamente un atributo. En segundo lugar, no estoy seguro de que haya leído el código correctamente, .Anymétodo llamado en el resultado del GetCustomAttrubutesmétodo. Entonces el tipo de propertiesWithMyAttributeserá la colección de las propiedades. Mira el ejemplo en dotnetfiddle (agrego el enlace a la respuesta).
senteper
1
Puede reemplazar .Where con .Any, ya que .Any también permite lambdas.
PRMan