Not getting fields from GetType().GetFields with BindingFlag.Default
Asked Answered
B

7

25

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 I'm 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?

Basset answered 20/7, 2009 at 19:43 Comment(0)
S
56

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

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

Edit again: Win.

Edit 3/22/13: Used Concat instead of Union. Since we are specifying BindingFlags.DeclaredOnly and a type's BaseType cannot equal itself, Union is not needed and is more expensive.

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).Concat(GetAllFields(t.BaseType));
}
Shlomo answered 20/7, 2009 at 19:47 Comment(5)
Wouldn't make much of a difference anyway, since the fields are not static.Insurmountable
I tried: FieldInfo[] fields = Obj.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy); But it still dosen't work.Basset
Well i tried: FieldInfo[] fields = Obj.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy); But still no luck.Basset
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)Basset
@Patrick, just call the method I wrote above with obj.GetType()Shlomo
F
4

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

Footpoundsecond answered 20/7, 2009 at 20:1 Comment(0)
M
3

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)

Mister answered 20/7, 2009 at 20:9 Comment(1)
You should include flags |= BindingFlags.DeclaredOnly in the implementation or you'll get duplicates.Shlomo
U
2

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.

Uncertain answered 20/7, 2009 at 19:45 Comment(2)
Objects created of a derived type do have the base type's instance fields included in it.Shlomo
So there is no way for the GetType() to get the fields then?Basset
E
0

If you just want the names for both properties and fields, use

private static IEnumerable<string > GetAllFieldsAndProperties(Type t)
{
  if (t == null)
    return Enumerable.Empty<string>();

  BindingFlags flags = BindingFlags.Public 
    | BindingFlags.NonPublic 
    | BindingFlags.Static 
    | BindingFlags.Instance 
    | BindingFlags.DeclaredOnly;
  return t.GetFields(flags).Select(x=>x.Name)
    .Union(GetAllFieldsAndProperties(t.BaseType))
    .Union(t.GetProperties(flags).Select(x=>x.Name));
}
Estivation answered 31/1, 2011 at 8:1 Comment(0)
B
0

Enumeration of all type fields including private members from base classes.

public static IEnumerable<FieldInfo> EnumerateFields(this Type type, BindingFlags flags) =>
   type.BaseType?.EnumerateFields(flags)
       .Concat(type.GetFields(flags | BindingFlags.DeclaredOnly)) ??
   type.EnumerateFields(flags);
Baresark answered 3/7, 2017 at 21:2 Comment(2)
Please try to avoid just dumping code as an answer and try to explain what it does and why. Your code might not be obvious for people who do not have the relevant coding experience. Please edit your answer to include clarification, context and try to mention any limitations, assumptions or simplifications in your answer.Nalchik
Thanks, description has been added.Baresark
A
0

Based on Sam's and Steve's answers this is what it worked for me:


public static List<Variance> DetailedCompare<T>(this T val1, T val2)
    {
        try
        {
            List<Variance> variances = new List<Variance>();

            BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic |
                                 BindingFlags.Static | BindingFlags.Instance |
                                 BindingFlags.DeclaredOnly;
            FieldInfo[] fi = val1.GetType().GetFields(flags);

            foreach (FieldInfo f in fi)
            {
                var previousValue = f.GetValue(val1);
                var newValue = f.GetValue(val2); 

                if (!Equals(previousValue, newValue))
                {
                    Variance v = new Variance
                    {
                        Prop = f.Name,
                        PreviousValue = previousValue,
                        NewValue = newValue
                    };
                    variances.Add(v);
                }
            }
            return variances;
        }
        catch
        {
            return null;
        }
    }
Arietta answered 2/9, 2022 at 18:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.