Impossibile ottenere i campi da GetType().GetFields con BindingFlag.Default
-
18-09-2019 - |
Domanda
Sto utilizzando le classi Reflection per ottenere tutti i campi all'interno di un determinato oggetto.Il mio problema però è che funziona perfettamente quando i campi si trovano all'interno di una classe normale, come:
class test
{
string test1 = string.Empty;
string test2 = string.Empty;
}
Qui ottengo sia test1 che test2, il mio problema è che utilizzo l'astrazione e quindi diverse classi combinate.
Ho qualcosa del tipo:
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;
}
Ma quando lo eseguo, non recupero i campi da GetType().GetFields(BindingFlag.Default)
.
Ognuno di questi campi ha anche una proprietà, get; set;
ad esso allegato.Quando eseguo il codice, ottengo le proprietà fino a test1 ma non i campi effettivi.
Questo è il codice con cui sto cercando di ottenere i campi:
FieldInfo[] fields = Obj.GetType().GetFields(BindingFlags.Default);
foreach (FieldInfo field in fields)
Ho anche provato:
FieldInfo[] fields = Obj.GetType().GetFields(BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.NonPublic
| BindingFlags.Static);
Utilizzo lo stesso codice per le proprietà:
PropertyInfo[] properties = Obj.GetType().GetProperties(BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.NonPublic
| BindingFlags.Static);
foreach (PropertyInfo property in properties)
Qualche idea sul perché ottengo le proprietà dalle classi astratte ma non dai campi?
Soluzione
Modificare:Ottenere privato membri del tipo base, è necessario:
typeof(T).BaseType.GetFields(...)
Modifica di nuovo:Vincita.
Modifica 22/03/13:Usato Concat
invece di Union
.Poiché stiamo specificando BindingFlags.DeclaredOnly
e un tipo BaseType
non può eguagliare se stesso, Union
non è necessario ed è più costoso.
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));
}
Altri suggerimenti
Un tipo che eredita un altro tipo non può vedere parti intime di quel altro tipo, che possa vedere protetta, parti interne e pubbliche. Si consideri il seguente codice:
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));
}
}
L'output di questo programma è il seguente:
B Fields:
ProtectedString
A Fields:
PrivateString
ProtectedString
Quindi, il tipo A
ha due campi; PrivateString
e ProtectedString
. Tipo B
ha uno; ProtectedString
, che eredita da A
. Se si desidera "raggiungere" PrivateString
attraverso il tipo B
, è necessario per navigare verso il suo tipo di base (b.GetType().BaseType
).
Nota però, che anche se i rapporti di tipo B
di avere un campo chiamato ProtectedString
, questo campo non è ancora dichiarata in B
; si è dichiarata in A
. Questo può essere esaminata aggiungendo BindingFlags.DeclaredOnly
alle chiamate GetFields
nel programma di esempio di cui sopra; GetFields
tornerà nessun campo per B
, e due per A
.
Tradotto al tuo esempio di codice, questo significa che il tipo test3
non contiene i campi test2
e test3
, dal momento che sono private al tipo test2
(la somiglianza dei nomi dei campi ei nomi tipo rendono questa frase un po 'confuso, io sono paura) .a
È possibile utilizzare questo metodo di estensione per attraversare in modo ricorsivo gerarchia di ereditarietà di un tipo tutta la strada fino a opporsi, in modo efficace il ritorno tutti i campi del tipo e tutti i suoi antenati:
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 testata, YMMV)
Le proprietà sono ereditate, i campi non sono. campi protetti sono visibili a discendente classi, ma non ereditato da loro. In altre parole, la classe discendente ha effettivamente le proprietà della sua classe base, ma è solo in grado di vedere i campi.
Se si desidera solo i nomi per entrambe le proprietà ei campi, utilizzare
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));
}
Enumerazione di tutti i campi di tipo inclusi i membri privati delle classi base.
public static IEnumerable<FieldInfo> EnumerateFields(this Type type, BindingFlags flags) =>
type.BaseType?.EnumerateFields(flags)
.Concat(type.GetFields(flags | BindingFlags.DeclaredOnly)) ??
type.EnumerateFields(flags);