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?

Foi útil?

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);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top