Não obtendo campos de GetType (). GetFields com BindingFlag.Default
-
18-09-2019 - |
Pergunta
Eu estou usando as classes de reflexão, a fim de obter todos os campos dentro de um determinado objeto. Meu problema, porém, é que ele funciona perfeitamente quando os campos estão dentro de uma classe normal, como:
class test
{
string test1 = string.Empty;
string test2 = string.Empty;
}
Aqui eu recebo tanto test1 e test2, o meu problema é que eu uso abstração e, portanto, várias classes combinadas.
Eu tenho algo como:
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;
}
Mas quando eu executá-lo, eu não obter os campos de volta da GetType().GetFields(BindingFlag.Default)
.
Todos desses campos também tem uma propriedade, get; set;
ligado a ele.
Quando eu executar o código, eu recebo as propriedades todo o caminho de volta para test1, mas não os campos reais.
Este é o código que eu estou tentando obter os campos com:
FieldInfo[] fields = Obj.GetType().GetFields(BindingFlags.Default);
foreach (FieldInfo field in fields)
Eu também tentei:
FieldInfo[] fields = Obj.GetType().GetFields(BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.NonPublic
| BindingFlags.Static);
Eu uso o mesmo código para as propriedades:
PropertyInfo[] properties = Obj.GetType().GetProperties(BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.NonPublic
| BindingFlags.Static);
foreach (PropertyInfo property in properties)
Todas as ideias por que recebo as propriedades das classes abstratas, mas não os campos?
Solução
Edit: Para obter privada membros do tipo base, você tem que:
typeof(T).BaseType.GetFields(...)
Editar novamente:. Win
Editar 3/22/13: Concat
usado em vez de Union
. Uma vez que estamos especificando BindingFlags.DeclaredOnly
e BaseType
de um tipo não pode ser igual a si mesmo, Union
não é necessário e é mais caro.
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));
}
Outras dicas
Um tipo que herda de outro tipo não pode ver partes íntimas do que outro tipo, pode ver protegida, interna e partes públicas. Considere o seguinte código:
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));
}
}
A saída deste programa é o seguinte:
B Fields:
ProtectedString
A Fields:
PrivateString
ProtectedString
Assim, o tipo A
tem dois campos; PrivateString
e ProtectedString
. Tipo B
tem um; ProtectedString
, que herda de A
. Se você deseja PrivateString
"alcance" por meio do tipo B
, você precisará navegar para o seu tipo de base (b.GetType().BaseType
).
Note, porém, que, mesmo se os relatórios tipo B
ter um campo chamado ProtectedString
, este campo ainda não está declarado em B
; ela é declarada em A
. Isto pode ser examinada através da adição de BindingFlags.DeclaredOnly
para as chamadas GetFields
no programa de exemplo acima; GetFields
retornará nenhum campo para B
, e dois para A
.
Traduzido para o exemplo de código, isso significa que o tipo test3
não contém o test2
campos e test3
, uma vez que são privados para o tipo test2
(a semelhança dos nomes de campo e nomes de tipo tornar essa frase um tanto confuso, estou medo) .a
Você pode usar este método de extensão para recursivamente atravessar um tipo de hierarquia de herança todo o caminho até objeto, retornando de forma eficaz todos os campos do tipo e todos os seus antepassados:
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;
}
}
(não testado, YMMV)
As propriedades são herdadas, os campos não são. campos protegidos são visíveis para classes descendentes, mas não herdado por eles. Em outras palavras, a classe descendente realmente tem as propriedades de sua classe base, mas é apenas capaz de ver os campos.
Se você quiser apenas os nomes para as duas propriedades e campos, uso
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));
}
A enumeração de todos os campos de tipo incluindo membros privados de classes base.
public static IEnumerable<FieldInfo> EnumerateFields(this Type type, BindingFlags flags) =>
type.BaseType?.EnumerateFields(flags)
.Concat(type.GetFields(flags | BindingFlags.DeclaredOnly)) ??
type.EnumerateFields(flags);