Не получать поля из GetType().GetFields с BindingFlag.Default

Я использую классы Reflection для того, чтобы получить все поля внутри определенного объекта. Моя проблема, однако, заключается в том, что он отлично работает, когда поля находятся внутри нормального класса, например:

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

Здесь я получаю как test1, так и test2, моя проблема в том, что я использую абстракцию и, следовательно, несколько классов вместе.

Я получил что-то вроде:

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;
}

Но когда я запускаю его, я не получаю поля от GetType().GetFields(BindingFlag.Default),

Каждый из этих полей также имеет свойство, get; set; прикреплен к нему. Когда я запускаю код, я получаю свойства вплоть до test1, но не фактические поля.

Это код, который я пытаюсь получить поля с:

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

Я также попробовал:

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

Я использую тот же код для свойств:

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

foreach (PropertyInfo property in properties)

Любые идеи, почему я получаю свойства из абстрагированных классов, но не полей?

7 ответов

Решение

Изменить: Чтобы получить частные члены базового типа, вам необходимо:

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

Отредактируйте снова: Win.

Изменить 22.03.13: Используется Concat вместо Union, Поскольку мы указываем BindingFlags.DeclaredOnly и тип BaseType не может сравниться, Union не нужен и стоит дороже.

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));
}

Тип, который наследует другой тип, не может видеть закрытые части этого другого типа, он может видеть защищенную, внутреннюю и открытую части. Рассмотрим следующий код:

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));

    }
}

Вывод этой программы следующий:

B Fields:
ProtectedString
A Fields:
PrivateString
ProtectedString

Итак, тип A имеет два поля; PrivateString а также ProtectedString, Тип B имеет один; ProtectedStringчто он наследует от A, Если вы хотите "достичь" PrivateString через тип B, вам нужно будет перейти к его базовому типу (b.GetType().BaseType).

Обратите внимание, что даже если тип B сообщает, чтобы иметь поле под названием ProtectedStringэто поле еще не объявлено в B; объявлено в A, Это можно проверить, добавив BindingFlags.DeclaredOnly к GetFields звонки в приведенном выше примере программы; GetFields не вернет поля для Bи два для A,

В переводе на ваш пример кода это означает, что тип test3 не содержит полей test2 а также test3, поскольку они являются частными для типа test2 (боюсь, сходство имен полей и имен типов делает это предложение несколько запутанным).a

Вы можете использовать этот метод расширения для рекурсивного обхода иерархии наследования типа вплоть до объекта, эффективно возвращая все поля типа и всех его предков:

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;
    }
}

(Не проверено, YMMV)

Свойства наследуются, поля нет. Защищенные поля видимы для классов-потомков, но не наследуются ими. Другими словами, класс-потомок на самом деле имеет свойства своего базового класса, но он просто способен видеть поля.

Если вы просто хотите имена как для свойств, так и для полей, используйте

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));
}

Перечисление всех полей типа, включая закрытые члены базовых классов.

public static IEnumerable<FieldInfo> EnumerateFields(this Type type, BindingFlags flags) =>
   type.BaseType?.EnumerateFields(flags)
       .Concat(type.GetFields(flags | BindingFlags.DeclaredOnly)) ??
   type.EnumerateFields(flags);

Основываясь на ответах Сэма и Стива, это сработало для меня:


      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;
        }
    }
Другие вопросы по тегам