Не получать поля из 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;
}
}