51

I've got a custom attribute that I want to apply to my base abstract class so that I can skip elements that don't need to be viewed by the user when displaying the item in HTML. It seems that the properties overriding the base class are not inheriting the attributes.

Does overriding base properties (abstract or virtual) blow away attributes placed on the original property?

From Attribute class Defination

[AttributeUsage(AttributeTargets.Property,
                Inherited = true,
                AllowMultiple = false)]
public class NoHtmlOutput : Attribute
{
}

From Abstract Class Defination

[NoHtmlOutput]
public abstract Guid UniqueID { get; set; }

From Concrete Class Defination

public override Guid UniqueID{ get{ return MasterId;} set{MasterId = value;}}

From class checking for attribute

        Type t = o.GetType();
        foreach (PropertyInfo pi in t.GetProperties())
        {
            if (pi.GetCustomAttributes(typeof(NoHtmlOutput), true).Length == 1)
                continue;
            // processing logic goes here
        }
Marty Trenouth
  • 3,712
  • 6
  • 34
  • 43

5 Answers5

72

Instead of calling PropertyInfo.GetCustomAttributes(...), you have to call the static method System.Attribute.GetCustomAttributes(pi,...), as in:

PropertyInfo info = GetType().GetProperties();

// this gets only the attributes in the derived class and ignores the 'true' parameter
object[] DerivedAttributes = info.GetCustomAttributes(typeof(MyAttribute),true);

// this gets all of the attributes up the heirarchy
object[] InheritedAttributes = System.Attribute.GetCustomAttributes(info,typeof(MyAttribute),true);
John Holliday
  • 1,268
  • 1
  • 11
  • 19
  • 2
    This should be the answer. Changing from `mytype.GetCustomAttributes()` to `System.Attribute.GetCustomAttribute()` makes the `inherit` parameter work like it's supposed to. Magic! – nathanchere Jul 12 '13 at 00:49
  • 4
    Doesn't work for me in .NET 4.6.1. Trying to get the attribute from the POCO class of a Entity Framework dynamic proxy (whose base type is the POCO class). – N73k Oct 19 '16 at 03:05
  • It has to be a Custom Attribute. Something like `DataMemberAttribute` will not be listed. – lordcheeto Feb 28 '18 at 09:29
  • @lordcheeto `DataMemberAttribute` is a custom attribute, as is anything else that inherits from `System.Attribute`. – Mike Marynowski Jun 28 '18 at 12:33
36

No, attributes are inherited.

It's the GetCustomAttributes() method that does not look at parent declarations. It only looks at attributes applied to the specified member. From the docs:

Remarks

This method ignores the inherit parameter for properties and events. To search the inheritance chain for attributes on properties and events, use the appropriate overloads of the Attribute..::.GetCustomAttributes method.

womp
  • 115,835
  • 26
  • 236
  • 269
0

Looks like it only happens when the overriding method also has the attribute .

http://msdn.microsoft.com/en-us/library/a19191fh.aspx

However, you can override attributes of the same type or apply additional attributes to the derived component. The following code fragment shows a custom control that overrides the Text property inherited from Control by overriding the BrowsableAttribute attribute applied in the base class. Visual Basic

Public Class MyControl
   Inherits Control
   ' The base class has [Browsable(true)] applied to the Text property.
   <Browsable(False)>  _
   Public Overrides Property [Text]() As String
      ...
   End Property 
   ...
End Class
0

Here is my attempt. This is an extension method on MemberInfo that manually walks up the inheritance hierarchy. This seems to be compatible with dynamic proxies...at least hose created by Castle anyway so I am assuming it would be compatible with any proxy library.

public static IEnumerable<T> GetCustomAttributes<T>(this MemberInfo instance)
{
    while (instance != null)
    {
        object[] attributes = instance.GetCustomAttributes(typeof(T), false);
        if (attributes.Length > 0)
        {
            return attributes.Cast<T>();
        }
        Type ancestor = instance.DeclaringType.BaseType;
        if (ancestor != null)
        {
            IEnumerable<MemberInfo> ancestormatches = ancestor.FindMembers(instance.MemberType, BindingFlags.Instance | BindingFlags.Public, 
                (m, s) =>
                {
                    return m.Name == (string)s;
                }, instance.Name);
            instance = ancestormatches.FirstOrDefault();
        }
        else
        {
            instance = null;
        }
    }
    return new T[] { };
}

And you would use it like this.

Type t = o.GetType();
foreach (PropertyInfo pi in t.GetProperties())
{
    IEnumerable<NoHtmlOutput> attributes = pi.GetCustomAttributes<NoHtmlOutput>();
    foreach (NoHtmlOutput attribute in attributes)
    {
      Console.WriteLine(attribute);
    }
}
Brian Gideon
  • 47,849
  • 13
  • 107
  • 150
0

You can use

PropertyInfo::GetCustomAttributes<T>(true)

which works fine, see example: https://dotnetfiddle.net/2IhEWH

so, there is no need to use static method

System.Attribute.GetCustomAttributes
overt
  • 1