2011-07-09 24 views
152

Ho una classe, chiamiamola Prenota con una proprietà denominata Nome. Con quella proprietà, ho associato un attributo.Riflessione - ottieni il nome e il valore dell'attributo sulla proprietà

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

Nel mio metodo principale, che sto utilizzando la riflessione e desiderio di ottenere coppia chiave-valore di ciascun attributo per ogni proprietà. Quindi, in questo esempio, mi aspetto di vedere "Autore" per il nome dell'attributo e "AuthorName" per il valore dell'attributo.

Domanda: Come si ottiene il nome e il valore dell'attributo sulle proprietà utilizzando Reflection?

+0

quello che accade quando si tenta di accedere alla proprietà di su tale oggetto attraverso la riflessione, Sei bloccato da qualche parte o vuoi codice di riflessione – kobe

risposta

187

Usa typeof(Book).GetProperties() per ottenere un array di PropertyInfo istanze. Quindi usa GetCustomAttribute() su ogni PropertyInfo per vedere se qualcuno di loro ha il tipo di attributo Author. Se lo fanno, puoi ottenere il nome della proprietà dalle informazioni sulla proprietà e i valori dell'attributo dall'attributo.

Qualcosa lungo queste linee di scansione di un tipo per le proprietà che hanno uno specifico tipo di attributo e restituiscono dati in un dizionario (questo può essere resa più dinamica tipi passano nella routine):

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

Speravo che non avrò per lanciare l'attributo. – developerdoug

+0

prop.GetCustomAttributes (true) restituisce solo un oggetto []. Se non si desidera eseguire il cast, è possibile utilizzare la riflessione sulle istanze dell'attributo. –

+0

Che cos'è AuthorAttribute qui? È una classe derivata da Attribute? @ Adam Markowitz –

15

È possibile utilizzare GetCustomAttributesData() e GetCustomAttributes():

var attributeData = typeof(Book).GetProperty("Name").GetCustomAttributesData(); 
var attributes = typeof(Book).GetProperty("Name").GetCustomAttributes(false); 
+3

qual è la differenza? –

+1

@PrimeByDesign Il primo trova * come * per creare un'istanza degli attributi applicati. Quest'ultimo istanzia realmente quegli attributi. – HappyNomad

79

Per avere tutti gli attributi di una proprietà in un uso dizionario questo:

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

Ricordarsi di passare da falso a vero se si desidera includere anche attributi ereditati.

+2

Questo fa effettivamente la stessa cosa della soluzione di Adam, ma è molto più conciso. –

+23

Aggiungi .OfType () all'espressione anziché a ToDictionary se hai solo bisogno di attributi Autore e vuoi saltare un cast futuro –

+0

Non genererà un'eccezione quando ci sono due attributi dello stesso tipo sulla stessa proprietà? – Konstantin

11

Se vuoi dire "per gli attributi che prendono un parametro, elencare i nomi e degli attributi-parametro-valore", allora questo è più facile in .NET 4.5 tramite l'API CustomAttributeData:

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

se basta vuoi uno specifico valore di attributo Per esempio Display Attribute puoi usare il seguente codice.

var pInfo = typeof(Book).GetProperty("Name") 
          .GetCustomAttributes(typeof(DisplayAttribute),false) 
          .Cast<DisplayAttribute>().FirstOrDefault(); 

var name = pInfo.Name; 
14

Ho risolto problemi simili scrivendo un generico proprietà extension Attributo Helper:

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

Usage:

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

Come posso ottenere l'attributo description da const 'Fields'? – Amir

+1

Otterrete un errore: \t 1775 Impossibile accedere al membro 'Namespace.FieldName' con un riferimento di istanza; qualificarlo invece con un nome tipo. Se è necessario, suggerisco di cambiare 'const' in 'readonly'. –

+1

Dovresti avere un voto molto più utile di quello, onestamente. È una risposta molto bella e utile a molti casi. –

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

Usage:

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

o:

//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(); 
2
private static Dictionary<string, string> GetAuthors() 
{ 
    return typeof(Book).GetProperties() 
     .SelectMany(prop => prop.GetCustomAttributes()) 
     .OfType<AuthorAttribute>() 
     .ToDictionary(attribute => attribute.Name, attribute => attribute.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 questo esempio ho usato DisplayName invece di autore perché ha un campo denominato 'DisplayName' per essere mostrato con un valore.

0

Ecco alcuni metodi statici che è possibile utilizzare per ottenere MaxLength o qualsiasi altro attributo.

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

} 

Utilizzando il metodo statico ...

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

sia utilizzando il metodo di estensione opzionale su un'istanza ...

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

o utilizzando il metodo statico completo per qualsiasi altro attributo (StringLength per esempio) ...

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

Ispirato dalla risposta di Mikael Engver.

2

Negromante.
Per coloro che hanno ancora a mantenere NET 2.0, o quelli che vogliono farlo senza LINQ: l'utilizzo

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

Esempio:

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

o semplicemente

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

a ottenere l'attributo da enum, sto usando:

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

// Utilizzando

var _message = Translate(code); 
Problemi correlati