Sprawdź, czy właściwość ma atrybut

158

Biorąc pod uwagę właściwość w klasie z atrybutami - jaki jest najszybszy sposób określenia, czy zawiera ona dany atrybut? Na przykład:

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

Jaka jest najszybsza metoda ustalenia, na przykład, że ma atrybut „IsIdentity”?

Otávio Décio
źródło

Odpowiedzi:

279

Nie ma szybkiego sposobu pobierania atrybutów. Ale kod powinien wyglądać tak (dzięki Aaronaught ):

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

Jeśli chcesz pobrać właściwości atrybutu, to

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
źródło
63
Jeśli potrzebujesz tylko sprawdzić istnienie atrybutu i nie pobrać z niego żadnych informacji, użycie Attribute.IsDefinedwyeliminuje jedną linię kodu i brzydkie tablice / rzutowanie.
Aaronaught,
4
Coś, na co właśnie natknąłem się z tym, to niektóre atrybuty mają inny typ niż ich nazwa. Na przykład „NotMapped” w System.ComponentModel.DataAnnotations.Schema jest używany tak jak [NotMapped]w klasie, ale aby go wykryć, musisz użyćAttribute.IsDefined(pi, typeof(NotMappedAttribute))
Qjimbo
2
Może być łatwiejsze w użyciu ogólnego przeciążenia:IsIdentity[] attr = pi.GetCustomAttributes<IsIdentity>(false);
Mojtaba
@Qjimbo (lub prawdopodobnie ktoś inny czytający) Atrybuty są zwykle używane bez części „Attribute” w ich nazwie, ale tak jest. Konwencja pozwala na jego wykluczenie, więc zazwyczaj rzeczywisty typ ma atrybut na końcu swojej nazwy, ale po prostu nie jest używany.
Jim Wolff
44

Jeśli używasz .NET 3.5, możesz spróbować z drzewami wyrażeń. To jest bezpieczniejsze niż refleksja:

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
źródło
7
Do Twojej wiadomości zostało zadane pytanie dotyczące Twojej odpowiedzi. stackoverflow.com/questions/4158996/ ...
Greg
12

Możesz użyć wspólnej (ogólnej) metody do odczytu atrybutu z danego elementu 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;
            }
Manish Basantani
źródło
7

Aby zaktualizować i / lub ulepszyć odpowiedź przez @Hans Passant, oddzieliłbym pobieranie własności na metodę rozszerzenia. Ma to dodatkową zaletę w postaci usunięcia nieprzyjemnego magicznego ciągu w metodzie 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();
        }
    }
}

Twój test jest następnie zredukowany do dwóch linii

var property = PropertyHelper<MyClass>.GetProperty(x => x.MyProperty);
Attribute.IsDefined(property, typeof(MyPropertyAttribute));
Seb
źródło
7

Jeśli próbujesz to zrobić w przenośnej bibliotece klas PCL (tak jak ja), oto jak możesz to zrobić :)

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

W razie potrzeby możesz następnie sprawdzić liczbę nieruchomości, które mają tę specjalną właściwość.

Ma AlTaiar
źródło
7

Można to teraz zrobić bez drzew wyrażeń i metod rozszerzających w bezpieczny sposób dzięki nowej funkcji języka C #, nameof()takiej jak ta:

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

nameof () została wprowadzona w C # 6

Jim Wolff
źródło
6

Możesz użyć metody Attribute.IsDefined

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

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

Możesz podać właściwość, której konkretnie szukasz, lub możesz iterować je wszystkie za pomocą odbicia, na przykład:

PropertyInfo[] props = typeof(YourClass).GetProperties();
Francis Musignac
źródło
To się nie kompiluje. Nie można używać [] wokół YourProperty lub YourAttribute
rolki
Każda poprzednia odpowiedź opierała się na założeniach dotyczących nazw klas, właściwości i atrybutów, których przestrzegałem.
Francis Musignac,
Wygląda na naprawioną.
rolki
2

To dość stare pytanie, ale użyłem

Moja metoda ma ten parametr, ale można ją zbudować:

Expression<Func<TModel, TValue>> expression

Następnie w metodzie to:

System.Linq.Expressions.MemberExpression memberExpression 
       = expression.Body as System.Linq.Expressions.MemberExpression;
Boolean hasIdentityAttr = System.Attribute
       .IsDefined(memberExpression.Member, typeof(IsIdentity));
Mark Schultheiss
źródło