【问题标题】:Reflection - get attribute name and value on property反射 - 获取属性的属性名称和值
【发布时间】:2011-10-02 00:49:31
【问题描述】:

我有一个类,我们称它为 Book 并带有一个名为 Name 的属性。有了这个属性,我就有了一个与之关联的属性。

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

在我的主要方法中,我使用反射并希望获取每个属性的每个属性的键值对。所以在这个例子中,我希望看到属性名称为“Author”,属性值为“AuthorName”。

问题:如何使用反射获取属性的属性名称和值?

【问题讨论】:

  • 当您尝试通过反射访问该对象上的属性时发生了什么,您是卡在某个地方还是想要用于反射的代码

标签: c# reflection propertyinfo


【解决方案1】:

使用typeof(Book).GetProperties() 获取PropertyInfo 实例的数组。然后在每个PropertyInfo 上使用GetCustomAttributes() 以查看其中是否有任何Author 属性类型。如果是这样,您可以从属性信息中获取属性的名称,并从属性中获取属性值。

沿着这些思路扫描具有特定属性类型的属性的类型并在字典中返回数据(请注意,这可以通过将类型传递到例程中变得更加动态):

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

【讨论】:

  • 我希望不必强制转换属性。
  • prop.GetCustomAttributes(true) 只返回一个对象[]。如果您不想强制转换,则可以对属性实例本身使用反射。
  • 这里的 AuthorAttribute 是什么?它是从 Attribute 派生的类吗? @亚当马科维茨
  • 是的。 OP 正在使用名为“作者”的自定义属性。请参阅此处的示例:msdn.microsoft.com/en-us/library/sw480ze8.aspx
  • 与所涉及的所有其他操作(空检查和字符串分配除外)相比,强制转换属性的性能成本是微不足道的。
【解决方案2】:

您可以使用GetCustomAttributesData()GetCustomAttributes()

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

【讨论】:

  • 有什么区别?
  • @PrimeByDesign 前者找出如何实例化应用的属性。后者实际上实例化了这些属性。
【解决方案3】:

要在字典中获取属性的所有属性,请使用:

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

如果您还想包含继承的属性,请记住将 false 更改为 true

【讨论】:

  • 这实际上与 Adam 的解决方案相同,但更加简洁。
  • 如果您只需要作者属性并想跳过未来的演员表,请将 .OfType() 附加到表达式而不是 ToDictionary
  • 同一个property上有两个同类型的属性时,不会抛出异常吗?
【解决方案4】:

如果您的意思是“对于带有一个参数的属性,列出属性名称和参数值”,那么在 .NET 4.5 中通过CustomAttributeData API 更容易:

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

【讨论】:

    【解决方案5】:

    如果您只想要一个特定的属性值,例如显示属性,您可以使用以下代码:

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

    【讨论】:

      【解决方案6】:

      我通过写一个通用扩展属性属性助手解决了类似的问题:

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

      用法:

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

      【讨论】:

      • 如何从 const Fields 获取描述属性?
      • 您将得到:错误 1775 无法使用实例引用访问成员“Namespace.FieldName”;而是用类型名称来限定它。如果您需要这样做,我建议将 'const' 更改为 'readonly'。
      • 老实说,你应该有比这更有用的投票。对于许多情况,这是一个非常好的和有用的答案。
      • 感谢@DavidLétourneau!一个人只能希望。好像你在这方面提供了一点帮助。
      • :) 你认为可以通过使用你的泛型方法来获得一个类的所有属性的值并将属性的值分配给每个属性吗?
      【解决方案7】:
      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);
          }
      }
      

      用法:

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

      或:

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

      【讨论】:

        【解决方案8】:
        private static Dictionary<string, string> GetAuthors()
        {
            return typeof(Book).GetProperties()
                .SelectMany(prop => prop.GetCustomAttributes())
                .OfType<AuthorAttribute>()
                .ToDictionary(a => a.GetType().Name.Replace("Attribute", ""), a => a.Name);
        }
        

        使用泛型的示例(目标框架 4.5)

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Reflection;
        
        private static Dictionary<string, string> GetAttribute<TAttribute, TType>(
            Func<TAttribute, string> valueFunc)
            where TAttribute : Attribute
        {
            return typeof(TType).GetProperties()
                .SelectMany(p => p.GetCustomAttributes())
                .OfType<TAttribute>()
                .ToDictionary(a => a.GetType().Name.Replace("Attribute", ""), valueFunc);
        }
        

        用法

        var dictionary = GetAttribute<AuthorAttribute, Book>(a => a.Name);
        

        【讨论】:

          【解决方案9】:
          foreach (var p in model.GetType().GetProperties())
          {
             var valueOfDisplay = 
                 p.GetCustomAttributesData()
                  .Any(a => a.AttributeType.Name == "DisplayNameAttribute") ? 
                      p.GetCustomAttribute<DisplayNameAttribute>().DisplayName : 
                      p.Name;
          }
          

          在此示例中,我使用 DisplayName 而不是 Author,因为它有一个名为“DisplayName”的字段要显示一个值。

          【讨论】:

            【解决方案10】:

            这里有一些静态方法可用于获取 MaxLength 或任何其他属性。

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

            使用静态方法...

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

            或者在实例上使用可选的扩展方法...

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

            或者对任何其他属性使用完整的静态方法(例如 StringLength)...

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

            受 Mikael Engver 的回答启发。

            【讨论】:

              【解决方案11】:

              死灵术。
              对于那些仍然需要维护 .NET 2.0 或想要在没有 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);
              }
              

              示例用法:

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

              或者干脆

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

              【讨论】:

                【解决方案12】:

                要从枚举中获取属性,我正在使用:

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

                // 使用

                 var _message = Translate(code);
                

                【讨论】:

                  【解决方案13】:

                  只是在寻找放置这段代码的正确位置。

                  假设您有以下属性:

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

                  并且您想要获取 ShortName 值。你可以这样做:

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

                  或者说一般性:

                  internal static string GetPropertyAttributeShortName(string propertyName)
                  {
                      return ((DisplayAttribute)(typeof(SensorsModel).GetProperty(propertyName).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
                  }
                  

                  【讨论】:

                    【解决方案14】:

                    虽然上述最受好评的答案确实有效,但我建议在某些情况下使用稍微不同的方法。

                    如果您的类有多个属性始终具有相同的属性,并且您希望将这些属性排序到字典中,方法如下:

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

                    这仍然使用强制转换,但确保强制转换始终有效,因为您只会获得“AuthorName”类型的自定义属性。 如果您有多个属性,上面的答案会出现强制转换异常。

                    【讨论】:

                      【解决方案15】:

                      我把它写成一个动态方法,因为我在整个应用程序中使用了很多属性。方法:

                      public static dynamic GetAttribute(Type objectType, string propertyName, Type attrType)
                          {
                              //get the property
                              var property = objectType.GetProperty(propertyName);
                      
                              //check for object relation
                              return property.GetCustomAttributes().FirstOrDefault(x => x.GetType() == attrType);
                          }
                      

                      用法:

                      var objectRelAttr = GetAttribute(typeof(Person), "Country", typeof(ObjectRelationAttribute));
                      
                      var displayNameAttr = GetAttribute(typeof(Product), "Category", typeof(DisplayNameAttribute));
                      

                      希望这对任何人都有帮助

                      【讨论】:

                        【解决方案16】:

                        如果您想获取具有自定义属性的属性,请尝试以下操作:

                        IEnumerable propertyInfos = properties.GetType().GetProperties();

                        PropertyInfo p = propertyInfos.Where(x => x.GetCustomAttribute() != null);

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2013-04-24
                          • 2011-02-15
                          • 2012-02-05
                          • 1970-01-01
                          • 2012-09-01
                          相关资源
                          最近更新 更多