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?

È stato utile?

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);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top