Überprüfen Sie, ob die Eigenschaft ein Attribut hat

158

Was ist der schnellste Weg, um festzustellen, ob eine Eigenschaft in einer Klasse mit Attributen ein bestimmtes Attribut enthält? Beispielsweise:

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

Was ist die schnellste Methode, um festzustellen, ob beispielsweise das Attribut "IsIdentity" vorhanden ist?

Otávio Décio
quelle

Antworten:

279

Es gibt keine schnelle Möglichkeit, Attribute abzurufen. Aber Code sollte so aussehen ( Dank an Aaronaught ):

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

Wenn Sie dann Attributeigenschaften abrufen müssen

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
quelle
63
Wenn Sie nur prüfen müssen, ob das Attribut vorhanden ist, und keine Informationen daraus abrufen müssen, werden mit using Attribute.IsDefinedeine Codezeile und die hässlichen Arrays / Castings entfernt.
Aaronaught
4
Etwas, auf das ich gerade gestoßen bin, ist, dass einige Attribute einen anderen Typ haben als ihr Attributname. Zum Beispiel wird "NotMapped" in System.ComponentModel.DataAnnotations.Schema wie [NotMapped]in der Klasse verwendet, aber um es zu erkennen, müssen Sie verwendenAttribute.IsDefined(pi, typeof(NotMappedAttribute))
Qjimbo
2
Könnte einfacher sein, die generische Überladung zu verwenden:IsIdentity[] attr = pi.GetCustomAttributes<IsIdentity>(false);
Mojtaba
@Qjimbo (oder wahrscheinlich jemand anderes, der liest) Attribute werden normalerweise ohne den Teil "Attribut" ihres Namens verwendet, können es aber sein. Mit einer Konvention können Sie sie ausschließen. Daher hat der tatsächliche Typ normalerweise ein Attribut am Ende seines Namens, wird jedoch nicht verwendet.
Jim Wolff
44

Wenn Sie .NET 3.5 verwenden, können Sie es mit Ausdrucksbäumen versuchen. Es ist sicherer als Nachdenken:

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
quelle
7
Zu Ihrer Information wurde eine Frage zu Ihrer Antwort gestellt. stackoverflow.com/questions/4158996/…
Greg
12

Sie können eine allgemeine Methode verwenden, um Attribute über eine bestimmte MemberInfo zu lesen

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
quelle
7

Um die Antwort von @Hans Passant zu aktualisieren und / oder zu verbessern, würde ich das Abrufen der Eigenschaft in eine Erweiterungsmethode aufteilen. Dies hat den zusätzlichen Vorteil, dass die böse magische Zeichenfolge in der Methode GetProperty () entfernt wird.

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

Ihr Test wird dann auf zwei Zeilen reduziert

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

Wenn Sie versuchen, dies in einer PCL für tragbare Klassenbibliotheken (wie ich) zu tun, können Sie dies folgendermaßen tun :)

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

Sie können dann bei Bedarf die Anzahl der Eigenschaften überprüfen, die über diese spezielle Eigenschaft verfügen.

Hat AlTaiar
quelle
7

Dies kann jetzt ohne Ausdrucksbäume und Erweiterungsmethoden typsicher mit der neuen C # -Funktion nameof()wie folgt durchgeführt werden:

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

nameof () wurde in C # 6 eingeführt

Jim Wolff
quelle
6

Sie können die Attribute.IsDefined-Methode verwenden

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

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

Sie können die Eigenschaft bereitstellen, nach der Sie speziell suchen, oder Sie können alle mithilfe von Reflexion durchlaufen, z. B.:

PropertyInfo[] props = typeof(YourClass).GetProperties();
Francis Musignac
quelle
Dies wird nicht kompiliert. Sie können [] nicht für YourProperty oder YourAttribute verwenden
rollt
In jeder vorherigen Antwort wurden Annahmen zu Klassen-, Eigenschafts- und Attributnamen verwendet, denen ich gefolgt bin.
Francis Musignac
Erscheint jetzt behoben.
rollt
2

Dies ist eine ziemlich alte Frage, die ich aber verwendet habe

Meine Methode hat diesen Parameter, aber er könnte erstellt werden:

Expression<Func<TModel, TValue>> expression

Dann in der Methode dies:

System.Linq.Expressions.MemberExpression memberExpression 
       = expression.Body as System.Linq.Expressions.MemberExpression;
Boolean hasIdentityAttr = System.Attribute
       .IsDefined(memberExpression.Member, typeof(IsIdentity));
Mark Schultheiss
quelle