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

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
}

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

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

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

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ść.


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


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

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));
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.