Get attribute values from property and list values without knowing the attribute type

19,361

If you mean "given an attribute that takes one parameter, give me that value", for example:

[DisplayName("abc")] <===== "abc"
[Browsable(true)] <===== true

then this is easiest in .NET 4.5, via the new CustomAttributeData API:

using System.ComponentModel;
using System.Reflection;

public static class Program
{
    static void Main()
    {
        PropertyInfo prop = typeof(Foo).GetProperty("Bar");
        var val = GetPropertyAttributes(prop, "DisplayName");
    }
    public static object GetPropertyAttributes(PropertyInfo prop, string attributeName)
    {
        // look for an attribute that takes one constructor argument
        foreach(CustomAttributeData attribData in prop.GetCustomAttributesData()) 
        {
            string typeName = attribData.Constructor.DeclaringType.Name;
            if(attribData.ConstructorArguments.Count == 1 &&
                (typeName == attributeName || typeName == attributeName + "Attribute"))
            {
                return attribData.ConstructorArguments[0].Value;
            }
        }
        return null;
    }
}

class Foo
{
    [DisplayName("abc")]
    public string Bar { get; set; }
}
Share:
19,361
Steve Coleman
Author by

Steve Coleman

.NET developer C#, ASP/NET. MVC, WPF, WIN FORMS PL\SQL

Updated on August 01, 2022

Comments

  • Steve Coleman
    Steve Coleman over 1 year

    I want to pass in the attribute name and return the value. This will be in a generic util and it will not know the attribute type.

    Update This is the actual working code if someone needs to do this. I needed a way to have the core code parse the attributes without knowing what they were.

    public void LoadPropertiesToGrid(BaseGridPropertyModel model)
    {
        foreach (PropertyInfo prop in ReflectionUtil.FindPublicPropeties(model))
        {
            object editTyp = ReflectionUtil.GetPropertyAttributes(prop, "EditorType"); 
            object rowIdx = ReflectionUtil.GetPropertyAttributes(prop, "ColIndex");
            object name = ReflectionUtil.GetPropertyAttributes(prop, "Name");
            object visible = ReflectionUtil.GetPropertyAttributes(prop, "Visible");
            ConfigureColumn((string) name, (int) rowIdx, (bool) visible, (string) editTyp);
         }
    }
    [Serializable]
    public class CanvasPropertiesViewModel : BaseGridPropertyModel
    {
        [PropertiesGrid(Name = "TEsting Name 0", ColIndex = 0)]
        public string StringData1 { get; set; }
    
        [PropertiesGrid(Name = "TEsting Name 2", ColIndex = 2)]
        public string StringData2 { get; set; }
    
        [PropertiesGrid(Name = "TEsting Name 1", ColIndex = 1)]
        public string StringData3 { get; set; }
    }
    [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
    public sealed class PropertiesGridAttribute : Attribute
    {
        /// <summary>
        /// Editor type
        /// </summary>
        public Type EditorType { get; set; }
    
        /// <summary>
        /// Sets Column Index
        /// </summary>
        public int ColIndex { get; set; }
    
        /// <summary>
        /// Visible to Grid
        /// </summary>
        public bool Visible { get; set; }
    
        /// <summary>
        /// Dispaly Name of the property
        /// </summary>
        public string Name { get; set; }
    }
    public static object GetPropertyAttributes(PropertyInfo prop, string attributeName)
    {
        // look for an attribute that takes one constructor argument
        foreach (CustomAttributeData attribData in prop.GetCustomAttributesData())
        {
            string typeName = attribData.Constructor.DeclaringType.Name;
            //if (attribData.ConstructorArguments.Count == 1 && (typeName == attributeName || typeName == attributeName + "Attribute"))
            //{
            //    return attribData.ConstructorArguments[0].Value;
            //}
            foreach (CustomAttributeNamedArgument att in attribData.NamedArguments)
            {
                if(att.GetPropertyValue<string>("MemberName") == attributeName)
                {
                    return att.TypedValue.Value;
                }
            }
        }
        return null;
    }
    
    //PropertyExpressionParser 
    public static TRet GetPropertyValue<TRet>(this object obj, string propertyPathName)
    {
        if (obj == null)
        {
            throw new ArgumentNullException("obj");
        }
    
        string[] parts = propertyPathName.Split('.');
        string path = propertyPathName;
        object root = obj;
    
        if (parts.Length > 1)
        {
            path = parts[parts.Length - 1];
            parts = parts.TakeWhile((p, i) => i < parts.Length - 1).ToArray();
            string path2 = String.Join(".", parts);
            root = obj.GetPropertyValue<object>(path2);
        }
    
        var sourceType = root.GetType();
        var value = (TRet)sourceType.GetProperty(path).GetValue(root, null);
        return value;
    }
    
  • Steve Coleman
    Steve Coleman over 11 years
    This would put me in the same position of knowing the interface type. Though I could create the interface in the core code to access the values...
  • Steve Konves
    Steve Konves over 11 years
    In essence, knowing that a class (in this can a custom attribute) has a Value property is the same as declaring an interface with a Value property. If you don't have the ability to implement an interface with your custom attributes, you can still use reflection to find a property named Value.
  • Steve Coleman
    Steve Coleman over 11 years
    This works but it was not quite what I needed. I updated my questions with the answer based on your code. Thanks you got what I needed!
  • Steve Coleman
    Steve Coleman over 11 years
    Yep, That's what I did. Thanks for the help.