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"?

Otávio Décio
fuente

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
}
Hans Passant
fuente
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;
    }
}
Darin Dimitrov
fuente
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;
            }
Manish Basantani
fuente
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));
Seb
fuente
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.

Tiene AlTaiar
fuente
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

Jim Wolff
fuente
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();
Francis Musignac
fuente
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));
Mark Schultheiss
fuente