web-dev-qa-db-fra.com

Ne pas obtenir les champs de GetType (). GetFields avec BindingFlag.Default

J'utilise les classes de réflexion pour obtenir tous les champs d'un objet. Cependant, mon problème est qu'il fonctionne parfaitement lorsque les champs sont à l'intérieur d'une classe normale, comme:

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

Ici, j'obtiens à la fois test1 et test2, mon problème est que j'utilise l'abstraction et donc plusieurs classes combinées.

J'ai quelque chose comme:

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

Mais quand je le lance, je ne récupère pas les champs de la GetType().GetFields(BindingFlag.Default).

Chacun de ces champs est également associé à une propriété, get; set;. Lorsque je lance le code, je récupère toutes les propriétés dans Test1, mais pas les champs réels.

C'est le code que j'essaie d'obtenir dans les champs:

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

J'ai aussi essayé:

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

J'utilise le même code pour les propriétés:

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

foreach (PropertyInfo property in properties)

Des idées pour lesquelles je récupère les propriétés des classes abstraites mais pas des champs?

24
Patrick

Edit: Pour obtenir private membres du type de base, vous devez:

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

Modifier à nouveau: Win.

Éditer 22/03/13: utilisé Concat au lieu de Union. Puisque nous spécifions BindingFlags.DeclaredOnly et que la variable BaseType d'un type ne peut pas être identique à elle-même, Union n'est pas nécessaire et coûte plus cher.

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));
}
47
Sam Harwell

Un type qui hérite d'un autre type ne peut pas voir les parties privées de cet autre type, il peut voir les parties protégées, internes et publiques. Considérons le code suivant:

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

    }
}

La sortie de ce programme est la suivante:

B Fields:
ProtectedString
A Fields:
PrivateString
ProtectedString

Donc, le type A a deux champs; PrivateString et ProtectedString. Le type B en a un; ProtectedString, qu'il hérite de A. Si vous souhaitez "atteindre" PrivateString par le type B, vous devrez accéder à son type de base (b.GetType().BaseType).

Notez cependant que même si le type B indique avoir un champ appelé ProtectedString, ce champ n'est toujours pas déclaré dans B; il est déclaré dans A. Cela peut être examiné en ajoutant BindingFlags.DeclaredOnly aux appels GetFields dans l'exemple de programme ci-dessus; GetFields ne renverra aucun champ pour B et deux pour A.

Traduit en exemple de code, cela signifie que le type test3 ne contient pas les champs test2 et test3, car ils sont privés du type test2 (la similarité des noms de champs et des noms de types rend cette phrase quelque peu déroutante, je le crains). une

4
Fredrik Mörk

Vous pouvez utiliser cette méthode d'extension pour parcourir de manière récursive la hiérarchie d'héritage d'un type jusqu'à l'objet, renvoyant ainsi tous les champs du type et tous ses ancêtres:

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

(Non testé, YMMV)

3
Jacob

Les propriétés sont héritées, les champs ne le sont pas. Les champs protégés sont visibles pour les classes descendantes, mais ne les héritent pas. En d'autres termes, la classe descendante a les propriétés de sa classe de base, mais elle est simplement capable de voir les champs.

2
David M

Si vous voulez juste les noms des propriétés et des champs, utilisez

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));
}
0
Carlo V. Dango

Énumération de tous les champs types, y compris les membres privés des classes de base.

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