Ne pas obtenir les champs de GetType (). GetFields avec BindingFlag.Default
-
18-09-2019 - |
Question
J'utilise les classes de réflexion afin d'obtenir tous les champs à l'intérieur d'un certain objet. Mon problème est cependant que cela 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, je reçois à la fois test1 et test2, mon problème est que j'utilise l'abstraction et donc plusieurs classes combinées.
Je suis 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 reçois pas les champs arrière de la GetType().GetFields(BindingFlag.Default)
.
Chacun de ces domaines ont également une propriété, get; set;
attaché.
Quand je lance le code, je reçois les propriétés tout le chemin du retour à test1 mais pas les champs réels.
Voici le code que je suis en train d'obtenir les champs avec:
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)
Toutes les idées pourquoi je reçois les propriétés des classes abstraites, mais pas les champs?
La solution
Edit: Pour obtenir privé membres du type de base, vous devez:
typeof(T).BaseType.GetFields(...)
Modifier à nouveau. Win
Modifier 22/03/13: Concat
Utilisé au lieu de Union
. Puisque nous spécifions BindingFlags.DeclaredOnly
et la BaseType
d'un type ne peut pas lui-même égal, Union
n'est pas nécessaire et est 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));
}
Autres conseils
Un type qui hérite d'un autre type ne peut pas voir les parties intimes de cet autre type, il peut voir protégé, les parties internes et publiques. Considérez 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 le suivant:
B Fields:
ProtectedString
A Fields:
PrivateString
ProtectedString
Ainsi, le type A
a deux champs; PrivateString
et ProtectedString
. Type B
a un; ProtectedString
, qu'il hérite de A
. Si vous souhaitez « atteindre » PrivateString
à travers le B
de type, vous devez accéder à son type de base (b.GetType().BaseType
).
Notez cependant que même si les rapports de B
type d'avoir un champ appelé ProtectedString
, ce champ 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 le programme ci-dessus de l'échantillon; GetFields
retournera pas de champs pour B
, et deux pour A
.
Traduit à votre exemple de code, cela signifie que le type test3
ne contient pas les champs test2
et test3
, car ils sont privés du test2
de type (la similitude des noms de champs et les noms de type font cette phrase un peu confus, je suis peur) .a
Vous pouvez utiliser cette méthode d'extension pour traverser récursive la hiérarchie d'héritage d'un type tout le chemin jusqu'à l'objet, retourner efficacement 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;
}
}
(Untested, YMMV)
Les propriétés sont héritées, les champs ne sont pas. Les champs protégés sont visibles pour les classes dérivées, mais pas hérité par eux. En d'autres termes, la classe descendante possède effectivement les propriétés de sa classe de base, mais il est juste capable de voir les champs.
Si vous voulez juste les noms pour les propriétés et les 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));
}
Enumeration de tous les champs de type y compris les membres privés de 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);