Не получать поля из 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)
Любые идеи, почему я получаю свойства из абстрактных классов, но не поля?
Ответы
Ответ 1
Изменить: Чтобы получить частные члены базового типа, вы должны:
typeof(T).BaseType.GetFields(...)
Изменить еще раз: Win.
Редактирование 3/22/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));
}
Ответ 2
Тип, который наследует другой тип, не может видеть частные части этого другого типа, он может видеть защищенные, внутренние и общедоступные части. Рассмотрим следующий код:
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
Ответ 3
Вы можете использовать этот метод расширения для рекурсивного прохождения иерархии наследования типов до объекта, эффективно возвращая все поля типа и всех его предков:
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)
Ответ 4
Свойства наследуются, а поля - нет. Защищенные поля видны классам потомков, но не унаследованы ими. Другими словами, класс потомков фактически обладает свойствами своего базового класса, но он просто может видеть поля.
Ответ 5
Если вам просто нужны имена для свойств и полей, используйте
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));
}
Ответ 6
Перечисление всех полей типа, включая частных членов из базовых классов.
public static IEnumerable<FieldInfo> EnumerateFields(this Type type, BindingFlags bindingFlags) =>
type.BaseType?.EnumerateFields(bindingFlags)
.Concat(type.GetFields(bindingFlags | BindingFlags.DeclaredOnly)) ??
type.EnumerateFields(bindingFlags);