2009-07-20 8 views
22

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 sono all'interno di una classe normale, come:Non recuperare i campi da GetType(). GetFields con BindingFlag.Default

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

Qui io ottenere sia test1 e test2, il mio problema è che io uso l'astrazione, e quindi diverse classi combinato.

ho avuto qualcosa di simile:

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 l'eseguo, non ottengo i campi indietro dal GetType().GetFields(BindingFlag.Default).

Ognuno di questi campi ha anche una proprietà, get; set; collegata ad esso. Quando eseguo il codice, ottengo le proprietà fino a test1 ma non i campi effettivi.

Questo è il codice che sto cercando di ottenere i campi con:

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

io uso 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 del motivo per cui ottengo le proprietà dalle classi astratte ma non dai campi?

risposta

44

Modifica: Per ottenere privati ​​ membri del tipo di base, è necessario:

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

Edit di nuovo: Win.

Modifica 22/03/13: Concat utilizzato anziché 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)); 
} 
+0

Non farebbe comunque molta differenza, poiché i campi non sono statici. –

+0

Ho provato: FieldInfo [] fields = Obj.GetType(). GetFields (BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy); Ma continua a non funzionare. – Patrick

+0

Beh, ho provato: FieldInfo [] fields = Obj.GetType(). GetFields (BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy); Ma ancora senza fortuna. – Patrick

2

Le proprietà sono ereditate, i campi no. I campi protetti sono visibili alle classi discendenti, ma non ereditati da essi. In altre parole, la classe discendente ha in realtà le proprietà della sua classe base, ma è solo in grado di vedere i campi.

+0

Gli oggetti creati di un tipo derivato * do * contengono i campi di istanza del tipo di base inclusi. –

+0

Quindi non è possibile che GetType() ottenga i campi? – Patrick

4

Un tipo che eredita un altro tipo non può vedere parti private di quell'altro tipo, può vedere parti protette, 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 

Così, il tipo A ha due campi; PrivateString e ProtectedString. Il tipo B ne ha uno; ProtectedString, che eredita da A. Se si desidera "raggiungere" PrivateString tramite il tipo B, sarà necessario navigare fino al relativo tipo di base (b.GetType().BaseType).

Nota che, anche se il tipo B riporta un campo denominato ProtectedString, questo campo non è ancora dichiarato in B; è dichiarato in A. Questo può essere esaminato aggiungendo BindingFlags.DeclaredOnly alle chiamate GetFields nel suddetto programma di esempio; GetFields non restituirà alcun campo per B e due per A.

Tradotto al tuo codice di esempio, ciò 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, ho ho paura) .a

3

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

(testato , YMMV)

+1

Dovresti includere 'flags | = BindingFlags.DeclaredOnly' nell'implementazione o tu otterrà i duplicati –

0

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

enumerazione di tutti i campi di tipo compresi i membri privati ​​di classi di base.

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

Si prega di cercare di evitare di scaricare il codice come risposta e cercare di spiegare cosa fa e perché. Il tuo codice potrebbe non essere ovvio per le persone che non hanno esperienza di codifica rilevante. Si prega di modificare la risposta per includere [chiarimento, contesto e cercare di menzionare eventuali limitazioni, ipotesi o semplificazioni nella risposta.] (Https://stackoverflow.com/help/how-to-answer) –

+0

Grazie, la descrizione è stata aggiunta. – Makeman

Problemi correlati