J'ai une classe, appelons-le Book avec une propriété appelée Name. Avec cette propriété, j'ai un attribut associé.
public class Book
{
[Author("AuthorName")]
public string Name
{
get; private set;
}
}
Dans ma méthode principale, j'utilise la réflexion et souhaite obtenir une paire clé-valeur de chaque attribut pour chaque propriété. Ainsi, dans cet exemple, je m'attendrais à voir "Auteur" pour le nom de l'attribut et "Nom de l'auteur" pour la valeur de l'attribut.
Question: Comment puis-je obtenir le nom et la valeur de l'attribut sur mes propriétés à l'aide de Reflection?
Utilisez typeof(Book).GetProperties()
pour obtenir un tableau d'instances PropertyInfo
. Utilisez ensuite GetCustomAttribute()
sur chaque PropertyInfo
pour voir si l’un d’eux a le type d’attribut Author
. S'ils le font, vous pouvez obtenir le nom de la propriété à partir des informations de la propriété et les valeurs d'attribut à partir de l'attribut.
Quelque chose dans ce sens pour rechercher dans un type les propriétés ayant un type d'attribut spécifique et pour renvoyer les données dans un dictionnaire (notez que cela peut être rendu plus dynamique en passant des types à la 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;
}
Pour obtenir tous les attributs d'une propriété dans un dictionnaire, utilisez ceci:
typeof(Book)
.GetProperty("Name")
.GetCustomAttributes(false)
.ToDictionary(a => a.GetType().Name, a => a);
souvenez-vous de passer de false à true si vous souhaitez également inclure des attributs hérités.
Si vous souhaitez uniquement une valeur d'attribut spécifique, par exemple Display Attribute, vous pouvez utiliser le code suivant:
var pInfo = typeof(Book).GetProperty("Name")
.GetCustomAttribute<DisplayAttribute>();
var name = pInfo.Name;
Vous pouvez utiliser GetCustomAttributesData()
et GetCustomAttributes()
:
var attributeData = typeof(Book).GetProperty("Name").GetCustomAttributesData();
var attributes = typeof(Book).GetProperty("Name").GetCustomAttributes(false);
J'ai résolu des problèmes similaires en écrivant un assistant d'attribut de propriété d'extension générique:
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"
Si vous voulez dire "pour les attributs qui prennent un paramètre, listez les noms d'attribut et la valeur de paramètre", c'est plus facile dans .NET 4.5 via 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; }
}
private static Dictionary<string, string> GetAuthors()
{
return typeof(Book).GetProperties()
.SelectMany(prop => prop.GetCustomAttributes())
.OfType<AuthorAttribute>()
.ToDictionary(attribute => attribute.Name, attribute => attribute.Name);
}
Nécromancie.
Pour ceux qui doivent encore maintenir .NET 2.0 ou ceux qui veulent le faire sans LINQ:
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);
}
Exemple d'utilisation:
System.Reflection.FieldInfo fi = t.GetField("PrintBackground");
wkHtmlOptionNameAttribute att = GetAttribute<wkHtmlOptionNameAttribute>(fi);
string name = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, delegate(wkHtmlOptionNameAttribute a){ return a.Name;});
ou simplement
string aname = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, a => a.Name );
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);
}
ou:
//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();
Je cherche juste le bon endroit pour mettre ce morceau de code.
disons que vous avez la propriété suivante:
[Display(Name = "Solar Radiation (Average)", ShortName = "SolarRadiationAvg")]
public int SolarRadiationAvgSensorId { get; set; }
Et vous voulez obtenir la valeur ShortName. Tu peux faire:
((DisplayAttribute)(typeof(SensorsModel).GetProperty(SolarRadiationAvgSensorId).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
Ou pour le dire en général:
internal static string GetPropertyAttributeShortName(string propertyName)
{
return ((DisplayAttribute)(typeof(SensorsModel).GetProperty(propertyName).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
}
pour obtenir l'attribut d'énum, j'utilise:
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();
}
// En utilisant
var _message = Translate(code);
Bien que les réponses les plus citées ci-dessus fonctionnent définitivement, je suggérerais d'utiliser une approche légèrement différente dans certains cas.
Si votre classe a plusieurs propriétés avec toujours le même attribut et que vous souhaitez obtenir ces attributs triés dans un dictionnaire, voici comment:
var dict = typeof(Book).GetProperties().ToDictionary(p => p.Name, p => p.GetCustomAttributes(typeof(AuthorName), false).Select(a => (AuthorName)a).FirstOrDefault());
Cela utilise toujours le transtypage mais garantit que le transtypage fonctionnera toujours car vous obtiendrez uniquement les attributs personnalisés du type "AuthorName". Si vous aviez plusieurs attributs, les réponses ci-dessus auraient une exception de distribution.
foreach (var p in model.GetType().GetProperties())
{
var valueOfDisplay =
p.GetCustomAttributesData()
.Any(a => a.AttributeType.Name == "DisplayNameAttribute") ?
p.GetCustomAttribute<DisplayNameAttribute>().DisplayName :
p.Name;
}
Dans cet exemple, j'ai utilisé DisplayName au lieu de Author car il comporte un champ nommé 'DisplayName' à afficher avec une valeur.
Voici quelques méthodes statiques que vous pouvez utiliser pour obtenir MaxLength ou tout autre attribut.
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);
}
}
En utilisant la méthode statique ...
var length = AttributeHelpers.GetMaxLength<Player>(x => x.PlayerName);
Ou en utilisant la méthode d'extension optionnelle sur une instance ...
var player = new Player();
var length = player.GetMaxLength(x => x.PlayerName);
Ou en utilisant la méthode statique complète pour tout autre attribut (StringLength par exemple) ...
var length = AttributeHelpers.GetPropertyAttributeValue<Player,string,StringLengthAttribute,Int32>(prop => prop.PlayerName,attr => attr.MaximumLength);
Inspiré par la réponse de Mikael Engver.