Controlla se la proprietà ha un attributo


158

Data una proprietà in una classe, con attributi: qual è il modo più veloce per determinare se contiene un dato attributo? Per esempio:

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

Qual è il metodo più veloce per determinare che ad esempio ha l'attributo "IsIdentity"?

Risposte:


280

Non esiste un modo rapido per recuperare gli attributi. Ma il codice dovrebbe apparire così (merito ad Aaronaught ):

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

Se è necessario recuperare le proprietà degli attributi, quindi

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
Se devi solo verificare l'esistenza dell'attributo e non recuperare alcuna informazione da esso, l'utilizzo Attribute.IsDefinedeliminerà una riga di codice e le brutte matrici / casting.
Aaronaught,

4
Qualcosa che ho appena incontrato con questo è che alcuni attributi hanno un tipo diverso dal loro nome di attributo. Ad esempio "NotMapped" in System.ComponentModel.DataAnnotations.Schema è usato come [NotMapped]nella classe ma per rilevarlo devi usareAttribute.IsDefined(pi, typeof(NotMappedAttribute))
Qjimbo

2
Potrebbe essere più semplice utilizzare il sovraccarico generico:IsIdentity[] attr = pi.GetCustomAttributes<IsIdentity>(false);
Mojtaba,

@Qjimbo (o probabilmente qualcun altro sta leggendo) Gli attributi sono di solito usati senza la parte "Attribute" del loro nome, ma possono esserlo. Una convenzione ti consente di escluderla, quindi di solito il tipo effettivo ha Attributo alla fine del suo nome, ma non viene usato.
Jim Wolff,

44

Se si utilizza .NET 3.5, è possibile provare con gli alberi delle espressioni. È più sicuro della riflessione:

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

7
Cordiali saluti, è stata posta una domanda sulla tua risposta. stackoverflow.com/questions/4158996/…
Greg,

12

È possibile utilizzare un metodo comune (generico) per leggere l'attributo su un determinato 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;
            }

7

Per aggiornare e / o migliorare la risposta di @Hans Passant, separerei il recupero della proprietà in un metodo di estensione. Questo ha l'ulteriore vantaggio di rimuovere la brutta stringa magica nel metodo 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();
        }
    }
}

Il test viene quindi ridotto a due righe

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

7

Se stai provando a farlo in una PCL della Portable Class Library (come me), ecco come puoi farlo :)

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

È quindi possibile verificare il numero di proprietà che dispongono di questa proprietà speciale, se necessario.


7

Questo ora può essere fatto senza alberi di espressione e metodi di estensione in modo sicuro con la nuova funzionalità C # nameof()come questa:

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

nameof () è stato introdotto in C # 6


6

È possibile utilizzare il metodo Attribute.IsDefined

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

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

Potresti fornire la proprietà che stai specificatamente cercando o puoi iterare tutti usando la riflessione, qualcosa come:

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

Questo non si compila. Non puoi usare [] intorno a YourProperty o YourAttribute
lancia il

Ogni risposta precedente ha utilizzato ipotesi su nomi di classi, proprietà e attributi che ho seguito.
Francis Musignac,

Appare risolto ora.
lancia il

2

Questa è una domanda piuttosto vecchia ma che ho usato

Il mio metodo ha questo parametro ma potrebbe essere costruito:

Expression<Func<TModel, TValue>> expression

Quindi nel metodo questo:

System.Linq.Expressions.MemberExpression memberExpression 
       = expression.Body as System.Linq.Expressions.MemberExpression;
Boolean hasIdentityAttr = System.Attribute
       .IsDefined(memberExpression.Member, typeof(IsIdentity));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.