Reflexion - Attributname und Wert für die Eigenschaft abrufen


253

Ich habe eine Klasse, nennen wir sie Buch mit einer Eigenschaft namens Name. Mit dieser Eigenschaft ist ein Attribut verknüpft.

public class Book
{
    [Author("AuthorName")]
    public string Name
    {
        get; private set; 
    }
}

In meiner Hauptmethode verwende ich Reflektion und möchte das Schlüsselwertpaar jedes Attributs für jede Eigenschaft erhalten. In diesem Beispiel würde ich erwarten, dass "Autor" als Attributname und "Autorenname" als Attributwert angezeigt werden.

Frage: Wie erhalte ich mithilfe von Reflection den Attributnamen und den Wert für meine Eigenschaften?


Was passiert, wenn Sie versuchen, durch Reflexion auf Eigenschaften auf diesem Objekt zuzugreifen, irgendwo hängen bleiben oder Code für Reflexion wollen
Kobe

Antworten:


307

Verwenden Sie typeof(Book).GetProperties()diese Option , um ein Array von PropertyInfoInstanzen abzurufen. Verwenden Sie dann GetCustomAttributes()jeweils, PropertyInfoum festzustellen, ob einer von ihnen den AuthorAttributtyp hat. In diesem Fall können Sie den Namen der Eigenschaft aus den Eigenschaftsinformationen und die Attributwerte aus dem Attribut abrufen.

Etwas in dieser Richtung, um einen Typ nach Eigenschaften zu durchsuchen, die einen bestimmten Attributtyp haben, und um Daten in einem Wörterbuch zurückzugeben (beachten Sie, dass dies durch Übergeben von Typen an die Routine dynamischer gestaltet werden kann):

public static Dictionary<string, string> GetAuthors()
{
    Dictionary<string, string> _dict = new Dictionary<string, string>();

    PropertyInfo[] props = typeof(Book).GetProperties();
    foreach (PropertyInfo prop in props)
    {
        object[] attrs = prop.GetCustomAttributes(true);
        foreach (object attr in attrs)
        {
            AuthorAttribute authAttr = attr as AuthorAttribute;
            if (authAttr != null)
            {
                string propName = prop.Name;
                string auth = authAttr.Name;

                _dict.Add(propName, auth);
            }
        }
    }

    return _dict;
}

16
Ich hatte gehofft, dass ich das Attribut nicht umsetzen muss.
Developerdoug

prop.GetCustomAttributes (true) gibt nur ein Objekt [] zurück. Wenn Sie nicht umwandeln möchten, können Sie die Attributinstanzen selbst reflektieren.
Adam Markowitz

Was ist AuthorAttribute hier? Ist es eine Klasse, die von Attribute abgeleitet ist? @ Adam Markowitz
Sarath Avanavu

1
Ja. Das OP verwendet ein benutzerdefiniertes Attribut mit dem Namen "Autor". Ein Beispiel finden Sie hier: msdn.microsoft.com/en-us/library/sw480ze8.aspx
Adam Markowitz

1
Die Leistungskosten für das Umwandeln des Attributs sind im Vergleich zu jeder anderen beteiligten Operation (mit Ausnahme der Nullprüfung und der Zuweisung von Zeichenfolgen) völlig unbedeutend.
SilentSin

112

Verwenden Sie Folgendes, um alle Attribute einer Eigenschaft in einem Wörterbuch abzurufen:

typeof(Book)
  .GetProperty("Name")
  .GetCustomAttributes(false) 
  .ToDictionary(a => a.GetType().Name, a => a);

Denken Sie daran, von falsezu zu wechseln , truewenn Sie auch geerbte Attribute einschließen möchten.


3
Dies entspricht praktisch der Lösung von Adam, ist jedoch weitaus prägnanter.
Daniel Moore

31
Fügen Sie .OfType <AuthorAttribue> () anstelle von ToDictionary an den Ausdruck an, wenn Sie nur Autorenattribute benötigen und eine zukünftige Besetzung überspringen möchten
Adrian Zanescu

2
Wird dies nicht eine Ausnahme auslösen, wenn sich zwei Attribute desselben Typs in derselben Eigenschaft befinden?
Konstantin

53

Wenn Sie nur einen bestimmten Attributwert möchten, können Sie zum Beispiel Anzeigeattribut den folgenden Code verwenden:

var pInfo = typeof(Book).GetProperty("Name")
                             .GetCustomAttribute<DisplayAttribute>();
var name = pInfo.Name;

30

Ich habe ähnliche Probleme gelöst, indem ich einen Generic Extension Property Attribute Helper geschrieben habe:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

public static class AttributeHelper
{
    public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(
        Expression<Func<T, TOut>> propertyExpression, 
        Func<TAttribute, TValue> valueSelector) 
        where TAttribute : Attribute
    {
        var expression = (MemberExpression) propertyExpression.Body;
        var propertyInfo = (PropertyInfo) expression.Member;
        var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute), true).FirstOrDefault() as TAttribute;
        return attr != null ? valueSelector(attr) : default(TValue);
    }
}

Verwendung:

var author = AttributeHelper.GetPropertyAttributeValue<Book, string, AuthorAttribute, string>(prop => prop.Name, attr => attr.Author);
// author = "AuthorName"

1
Wie kann ich ein Beschreibungsattribut von const erhalten Fields?
Amir

1
Sie erhalten Folgendes: Fehler 1775 Auf das Mitglied 'Namespace.FieldName' kann nicht mit einer Instanzreferenz zugegriffen werden. Qualifizieren Sie es stattdessen mit einem Typnamen. Wenn Sie dies tun müssen, schlage ich vor, 'const' in 'readonly' zu ändern.
Mikael Engver

1
Sie sollten ehrlich gesagt eine viel nützlichere Stimme haben. Es ist eine sehr schöne und nützliche Antwort auf viele Fälle.
David Létourneau

1
Danke @ DavidLétourneau! Man kann nur hoffen. Scheint, als hättest du ein bisschen dabei geholfen.
Mikael Engver

:) Glauben Sie, dass es möglich ist, den Wert aller Attribute für eine Klasse mithilfe Ihrer generischen Methode zu ermitteln und den Wert des Attributs jeder Eigenschaft zuzuweisen?
David Létourneau


12

Wenn Sie "für Attribute, die einen Parameter annehmen, die Attributnamen und den Parameterwert auflisten" meinen, ist dies in .NET 4.5 über die CustomAttributeDataAPI einfacher :

using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;

public static class Program
{
    static void Main()
    {
        PropertyInfo prop = typeof(Foo).GetProperty("Bar");
        var vals = GetPropertyAttributes(prop);
        // has: DisplayName = "abc", Browsable = false
    }
    public static Dictionary<string, object> GetPropertyAttributes(PropertyInfo property)
    {
        Dictionary<string, object> attribs = new Dictionary<string, object>();
        // look for attributes that takes one constructor argument
        foreach (CustomAttributeData attribData in property.GetCustomAttributesData()) 
        {

            if(attribData.ConstructorArguments.Count == 1)
            {
                string typeName = attribData.Constructor.DeclaringType.Name;
                if (typeName.EndsWith("Attribute")) typeName = typeName.Substring(0, typeName.Length - 9);
                attribs[typeName] = attribData.ConstructorArguments[0].Value;
            }

        }
        return attribs;
    }
}

class Foo
{
    [DisplayName("abc")]
    [Browsable(false)]
    public string Bar { get; set; }
}

3
private static Dictionary<string, string> GetAuthors()
{
    return typeof(Book).GetProperties()
        .SelectMany(prop => prop.GetCustomAttributes())
        .OfType<AuthorAttribute>()
        .ToDictionary(attribute => attribute.Name, attribute => attribute.Name);
}

2

Während die oben am häufigsten bewerteten Antworten definitiv funktionieren, würde ich in einigen Fällen einen etwas anderen Ansatz vorschlagen.

Wenn Ihre Klasse mehrere Eigenschaften mit immer demselben Attribut hat und Sie diese Attribute in ein Wörterbuch sortieren möchten, gehen Sie wie folgt vor:

var dict = typeof(Book).GetProperties().ToDictionary(p => p.Name, p => p.GetCustomAttributes(typeof(AuthorName), false).Select(a => (AuthorName)a).FirstOrDefault());

Dies verwendet weiterhin Cast, stellt jedoch sicher, dass der Cast immer funktioniert, da Sie nur die benutzerdefinierten Attribute vom Typ "AuthorName" erhalten. Wenn Sie mehrere Attribute über den Antworten hätten, würde eine Besetzungsausnahme auftreten.


1
public static class PropertyInfoExtensions
{
    public static TValue GetAttributValue<TAttribute, TValue>(this PropertyInfo prop, Func<TAttribute, TValue> value) where TAttribute : Attribute
    {
        var att = prop.GetCustomAttributes(
            typeof(TAttribute), true
            ).FirstOrDefault() as TAttribute;
        if (att != null)
        {
            return value(att);
        }
        return default(TValue);
    }
}

Verwendung:

 //get class properties with attribute [AuthorAttribute]
        var props = typeof(Book).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(AuthorAttribute)));
            foreach (var prop in props)
            {
               string value = prop.GetAttributValue((AuthorAttribute a) => a.Name);
            }

oder:

 //get class properties with attribute [AuthorAttribute]
        var props = typeof(Book).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(AuthorAttribute)));
        IList<string> values = props.Select(prop => prop.GetAttributValue((AuthorAttribute a) => a.Name)).Where(attr => attr != null).ToList();

1

Hier sind einige statische Methoden, mit denen Sie die MaxLength oder ein anderes Attribut abrufen können.

using System;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;

public static class AttributeHelpers {

public static Int32 GetMaxLength<T>(Expression<Func<T,string>> propertyExpression) {
    return GetPropertyAttributeValue<T,string,MaxLengthAttribute,Int32>(propertyExpression,attr => attr.Length);
}

//Optional Extension method
public static Int32 GetMaxLength<T>(this T instance,Expression<Func<T,string>> propertyExpression) {
    return GetMaxLength<T>(propertyExpression);
}


//Required generic method to get any property attribute from any class
public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(Expression<Func<T,TOut>> propertyExpression,Func<TAttribute,TValue> valueSelector) where TAttribute : Attribute {
    var expression = (MemberExpression)propertyExpression.Body;
    var propertyInfo = (PropertyInfo)expression.Member;
    var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute),true).FirstOrDefault() as TAttribute;

    if (attr==null) {
        throw new MissingMemberException(typeof(T).Name+"."+propertyInfo.Name,typeof(TAttribute).Name);
    }

    return valueSelector(attr);
}

}

Mit der statischen Methode ...

var length = AttributeHelpers.GetMaxLength<Player>(x => x.PlayerName);

Oder verwenden Sie die optionale Erweiterungsmethode für eine Instanz ...

var player = new Player();
var length = player.GetMaxLength(x => x.PlayerName);

Oder verwenden Sie die vollständige statische Methode für ein anderes Attribut (z. B. StringLength) ...

var length = AttributeHelpers.GetPropertyAttributeValue<Player,string,StringLengthAttribute,Int32>(prop => prop.PlayerName,attr => attr.MaximumLength);

Inspiriert von der Antwort des Mikael Engver.


1

Nekromantie.
Für diejenigen, die noch .NET 2.0 warten müssen oder die es ohne LINQ tun möchten:

public static object GetAttribute(System.Reflection.MemberInfo mi, System.Type t)
{
    object[] objs = mi.GetCustomAttributes(t, true);

    if (objs == null || objs.Length < 1)
        return null;

    return objs[0];
}



public static T GetAttribute<T>(System.Reflection.MemberInfo mi)
{
    return (T)GetAttribute(mi, typeof(T));
}


public delegate TResult GetValue_t<in T, out TResult>(T arg1);

public static TValue GetAttributValue<TAttribute, TValue>(System.Reflection.MemberInfo mi, GetValue_t<TAttribute, TValue> value) where TAttribute : System.Attribute
{
    TAttribute[] objAtts = (TAttribute[])mi.GetCustomAttributes(typeof(TAttribute), true);
    TAttribute att = (objAtts == null || objAtts.Length < 1) ? default(TAttribute) : objAtts[0];
    // TAttribute att = (TAttribute)GetAttribute(mi, typeof(TAttribute));

    if (att != null)
    {
        return value(att);
    }
    return default(TValue);
}

Anwendungsbeispiel:

System.Reflection.FieldInfo fi = t.GetField("PrintBackground");
wkHtmlOptionNameAttribute att = GetAttribute<wkHtmlOptionNameAttribute>(fi);
string name = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, delegate(wkHtmlOptionNameAttribute a){ return a.Name;});

oder einfach

string aname = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, a => a.Name );

0
foreach (var p in model.GetType().GetProperties())
{
   var valueOfDisplay = 
       p.GetCustomAttributesData()
        .Any(a => a.AttributeType.Name == "DisplayNameAttribute") ? 
            p.GetCustomAttribute<DisplayNameAttribute>().DisplayName : 
            p.Name;
}

In diesem Beispiel habe ich DisplayName anstelle von Author verwendet, da ein Feld mit dem Namen 'DisplayName' mit einem Wert angezeigt werden soll.


0

Um ein Attribut von enum zu erhalten, verwende ich:

 public enum ExceptionCodes
 {
  [ExceptionCode(1000)]
  InternalError,
 }

 public static (int code, string message) Translate(ExceptionCodes code)
        {
            return code.GetType()
            .GetField(Enum.GetName(typeof(ExceptionCodes), code))
            .GetCustomAttributes(false).Where((attr) =>
            {
                return (attr is ExceptionCodeAttribute);
            }).Select(customAttr =>
            {
                var attr = (customAttr as ExceptionCodeAttribute);
                return (attr.Code, attr.FriendlyMessage);
            }).FirstOrDefault();
        }

// Verwenden

 var _message = Translate(code);

0

Ich suche nur nach dem richtigen Ort, um diesen Code einzufügen.

Angenommen, Sie haben die folgende Eigenschaft:

[Display(Name = "Solar Radiation (Average)", ShortName = "SolarRadiationAvg")]
public int SolarRadiationAvgSensorId { get; set; }

Und Sie möchten den ShortName-Wert erhalten. Du kannst tun:

((DisplayAttribute)(typeof(SensorsModel).GetProperty(SolarRadiationAvgSensorId).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;

Oder um es allgemein zu machen:

internal static string GetPropertyAttributeShortName(string propertyName)
{
    return ((DisplayAttribute)(typeof(SensorsModel).GetProperty(propertyName).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.