views:

2639

answers:

5

Hi

I am using the Reflection classes in order to get all the fields inside a certain object. My problem however is that it works perfectly when the fields are inside a normal class, like:

class test
{
   string test1 = string.Empty;
   string test2 = string.Empty;
}

Here i get both test1 and test2, my problem is that i use abstraction and thus several classes combined.

I got something like:

class test3 : test2
{
   string test4 = string.Empty;
   string test5 = string.Empty;
}

class test2 : test1
{
   string test2 = string.Empty;
   string test3 = string.Empty;
}
class test1
{
   string test0 = string.Empty;
   string test1 = string.Empty;
}

But when i run it, i don't get the fields back from the GetType().GetFields(BindingFlag.Default).

Everyone of those fields also have a property, get; set attached to it. When i run the code, i get the properties all the way back to test1 but not the actual fields.

This is the code that im trying to get the fields with:

FieldInfo[] fields = Obj.GetType().GetFields(BindingFlags.Default);
foreach (FieldInfo field in fields)

I have also tried:

FieldInfo[] fields = Obj.GetType().GetFields(BindingFlags.Public 
                                             | BindingFlags.Instance 
                                             | BindingFlags.NonPublic 
                                             | BindingFlags.Static);

I use the same code for the properties:

PropertyInfo[] properties = Obj.GetType().GetProperties(BindingFlags.Public 
                                             | BindingFlags.Instance 
                                             | BindingFlags.NonPublic 
                                             | BindingFlags.Static);

foreach (PropertyInfo property in properties)

Any ideas why i get the properties from the abstracted classes but not the fields?

+2  A: 

Properties are inherited, fields are not. Protected fields are visible to descendant classes, but not inherited by them. In other words, the descendant class actually has the properties of its base class, but it is just able to see the fields.

David M
Objects created of a derived type *do* have the base type's instance fields included in it.
280Z28
So there is no way for the GetType() to get the fields then?
Patrick
+9  A: 

Edit: To get private members of the base type, you have to:

typeof(T).BaseType.GetFields(...)

Edit again: Win.

public static IEnumerable<FieldInfo> GetAllFields(Type t)
{
    if (t == null)
        return Enumerable.Empty<FieldInfo>();

    BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
    return t.GetFields(flags).Union(GetAllFields( t.BaseType));
}
280Z28
Wouldn't make much of a difference anyway, since the fields are not static.
Fredrik Mörk
I tried:FieldInfo[] fields = Obj.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy);But it still dosen't work.
Patrick
Well i tried:FieldInfo[] fields = Obj.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy);But still no luck.
Patrick
@Patrick: Now I got it and tested it :)
280Z28
Well i tried this: (still no luck with it)FieldInfo[] fields = Obj.GetType().BaseType.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy);Obj is an object passed from the calling class, since i dont know what class that is calling the function i used an object for it.void Check (object Obj)
Patrick
@Patrick, just call the method I wrote above with `obj.GetType()`
280Z28
It worked perfectly, thanks
Patrick
A: 

A type that inherits another type cannot see private parts of that other type, it can see protected, internal and public parts. Consider the following code:

class A
{
    // note that this field is private
    string PrivateString = string.Empty;
    // protected field
    protected string ProtectedString = string.Empty;
}

class B : A { }

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("B Fields:");
        B b = new B();
        b.GetType()
            .GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
            .ToList()
            .ForEach(f => Console.WriteLine(f.Name));

        Console.WriteLine("A Fields:");
        A a = new A();
        a.GetType()
            .GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
            .ToList()
            .ForEach(f => Console.WriteLine(f.Name));

    }
}

The output of this program is the following:

B Fields:
ProtectedString
A Fields:
PrivateString
ProtectedString

So, the type A has two fields; PrivateString and ProtectedString. Type B has one; ProtectedString, that it inherits from A. If you wish to "reach" PrivateString through the type B, you will need to navigate to its base type (b.GetType().BaseType).

Note though, that even if the type B reports to have a field called ProtectedString, this field is still not declared in B; it is declared in A. This can be examined by adding BindingFlags.DeclaredOnly to the GetFields calls in the above sample program; GetFields will return no fields for B, and two for A.

Translated to your code sample, this means that the type test3 does not contain the fields test2 and test3, since they are private to the type test2 (the similarity of the field names and type names make that sentence somewhat confusing, I am afraid).a

Fredrik Mörk
A: 

You can use this extension method to recursively traverse a type's inheritance hierarchy all the way up to object, effectively returning all fields of the type and all its ancestors:

public static class ReflectionExtensions
{
    public static IList<FieldInfo> GetAllFields(this Type type, BindingFlags flags)
    {
        if(type == typeof(Object)) return new List<FieldInfo>();

        var list = type.BaseType.GetAllFields(flags);
        // in order to avoid duplicates, force BindingFlags.DeclaredOnly
        list.AddRange(type.GetFields(flags | BindingFlags.DeclaredOnly));
        return list;
    }
}

(Untested, YMMV)

Jacob
You should include `flags |= BindingFlags.DeclaredOnly` in the implementation or you'll get duplicates.
280Z28