Comprobar si la propiedad tiene atributo


158

Dada una propiedad en una clase, con atributos, ¿cuál es la forma más rápida de determinar si contiene un atributo dado? Por ejemplo:

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

¿Cuál es el método más rápido para determinar que, por ejemplo, tiene el atributo "IsIdentity"?

Respuestas:


280

No hay una forma rápida de recuperar atributos. Pero el código debería verse así (crédito a Aaronaught ):

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

Si necesita recuperar propiedades de atributo, entonces

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
Si solo necesita verificar la existencia del atributo, y no recuperar ninguna información del mismo, el uso Attribute.IsDefinedeliminará una línea de código y las matrices / transmisiones feas.
Aaronaught

44
Algo que acabo de encontrar con esto es que algunos atributos tienen un tipo diferente a su nombre de atributo. Por ejemplo, "NotMapped" en System.ComponentModel.DataAnnotations.Schema se usa como [NotMapped]en la clase, pero para detectarlo debe usarloAttribute.IsDefined(pi, typeof(NotMappedAttribute))
Qjimbo

2
Podría ser más fácil usar la sobrecarga genérica:IsIdentity[] attr = pi.GetCustomAttributes<IsIdentity>(false);
Mojtaba

@Qjimbo (o probablemente alguien más leyendo) Los atributos generalmente se usan sin la parte "Atributo" de su nombre, pero pueden serlo. Una convención le permite excluirlo, por lo que generalmente el tipo real tiene Atributo al final de su nombre, pero simplemente no se usa.
Jim Wolff

44

Si está utilizando .NET 3.5, puede probar con árboles de expresión. Es más seguro que la reflexión:

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

77
Para su información, se ha hecho una pregunta sobre su respuesta. stackoverflow.com/questions/4158996/…
Greg

12

Puede usar un método común (genérico) para leer el atributo sobre una información de miembro dada

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

Para actualizar y / o mejorar la respuesta de @Hans Passant, separaría la recuperación de la propiedad en un método de extensión. Esto tiene el beneficio adicional de eliminar la cadena mágica desagradable en el 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();
        }
    }
}

Su prueba se reduce a dos líneas.

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

7

Si está tratando de hacer eso en una PCL de la Biblioteca de clases portátil (como yo), así es como puede hacerlo :)

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

Luego puede verificar la cantidad de propiedades que tienen esta propiedad especial si es necesario.


7

Esto ahora se puede hacer sin árboles de expresión y métodos de extensión de manera segura con la nueva característica C # nameof()como esta:

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

nameof () se introdujo en C # 6


6

Puede usar el 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...
}

Puede proporcionar la propiedad que está buscando específicamente o puede recorrerlos todos mediante la reflexión, algo así como:

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

Esto no se compila. No puede usar [] alrededor de YourProperty o YourAttribute
lanza el

Cada respuesta anterior ha utilizado supuestos sobre los nombres de clase, propiedad y atributo que seguí.
Francis Musignac

Parece arreglado ahora.
llega el

2

Esta es una pregunta bastante antigua pero solía

Mi método tiene este parámetro pero podría construirse:

Expression<Func<TModel, TValue>> expression

Entonces en el método esto:

System.Linq.Expressions.MemberExpression memberExpression 
       = expression.Body as System.Linq.Expressions.MemberExpression;
Boolean hasIdentityAttr = System.Attribute
       .IsDefined(memberExpression.Member, typeof(IsIdentity));
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.