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.