Getting all fields - including inherited ones - with a custom attribute
I've got a custom attribute BacksCache
that I'm using to mark fields in a class that need to be "defaulted" when certain conditions are met.
I'm using the following code:
Type type = obj.GetType();
FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy);
foreach (FieldInfo field in fields)
{
foreach (Attribute at in field.GetCustomAttributes(true))
if (at is BacksCache)
{
field.SetValue(obj, Utils.DefaultValue(field.FieldType));
}
}
This code works fine, provid开发者_JAVA技巧ed that the class inheritance hierarchy is flat. That is to say, if type
is the Type that declares all the attributed fields, everything's great. As soon as I have a class A
from which descends class B
(B : A
) and B
has some attributed fields things fall apart: only the fields declared by A
are detected and "defaulted".
The fields in question are private
or private volatile
, if that makes any difference.
Any ideas on how to fix this?
Beware of the small print for the BindingFlags.FlattenHierarchy option:
Specifies that public and protected static members up the hierarchy should be returned. Private static members in inherited classes are not returned. Static members include fields, methods, events, and properties. Nested types are not returned.
The "static" word in the bolded phrase is an oops, no private members are returned. To work around this you'll need to move up the inheritance chain through Type.BaseType.
Your wording is a bit confusing, which class is the base class A or B? And which one are you reflecting over, the base class or the derived class. I ask because I'm having trouble reproducing your issue. I've tried this and it works fine:
static void Main(string[] args)
{
foreach (var prop in typeof(Sub).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy))
{
foreach (var attrib in prop.GetCustomAttributes(typeof(DescriptionAttribute), false))
{
Console.WriteLine(prop.Name);
}
}
Console.ReadKey(true);
}
public class Base
{
[Description]
public int IntProp { get; set; }
}
public class Sub : Base
{
[Description]
public string StringProp { get; set; }
}
The output is correct, it shows both properties.
Furthermore, I just noticed you're dealing with Fields. I did this, and still got the correct output:
static void Main(string[] args)
{
foreach (var prop in typeof(Sub).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy))
{
foreach (var attrib in prop.GetCustomAttributes(typeof(DescriptionAttribute), false))
{
Console.WriteLine(prop.Name);
}
}
Console.ReadKey(true);
}
public class Base
{
[Description]
public int X;
[Description]
public int IntProp { get; set; }
}
public class Sub : Base
{
[Description]
public int Y;
[Description]
public string StringProp { get; set; }
}
Both X and Y get output to the console.
Attribute.GetCustomAttributes
will search for inherited values. The code to do so given your example would be:
foreach (Attribute at in Attribute.GetCustomAttributes(field, true))
{
// Test and set here
}
For further explanation see Weitao Su's post on GetCustomAttributes.
精彩评论