3 Stimmen

.NET, C#, Reflection: Auflistung der Felder eines Feldes, das selbst Felder hat

In .NET & C#, nehmen Sie an ClassB hat ein Feld vom Typ ClassA . Man kann einfach die Methode GetFields auflisten ClassB Felder. Ich möchte jedoch auch Liste der Felder dieser ClassB Felder, die selbst haben Felder. Zum Beispiel, ClassB Das Feld x hat Felder b , s und i . Ich möchte diese Felder (programmgesteuert) auflisten (wie in meinen Kommentaren im Code unten vorgeschlagen).

class ClassA
    {
    public  byte    b ;
    public  short   s ;
    public  int i ;
    }

class ClassB
    {
    public  long    l ;
    public  ClassA  x ;
    }

class MainClass
    {
    public static void Main ( )
        {
        ClassA myAObject = new ClassA () ;
        ClassB myBObject = new ClassB () ;

        //  My goal is this:
        //    ***Using myBObject only***, print its fields, and the fields
        //    of those fields that, *themselves*, have fields.
        //  The output should look like this:
        //    Int64   l
        //    ClassA  x
        //               Byte   b
        //               Int16  s
        //               Int32  i

        }
    }

0 Stimmen

Ist dies ein Kandidat für eine knorrige LINQ? :)

8voto

Martin Liversage Punkte 100306

Verwenden Sie die FieldInfo.FieldType um über den Typ der Felder in Ihrer Klasse nachzudenken. z.B.

fieldInfo.FieldType.GetFields();

Hier ist ein vollständiges Beispiel auf der Grundlage Ihres Codes, der Rekursion verwendet, falls Sie ClassZ innerhalb ClassA . Bei einem zyklischen Objektgraphen ist dies nicht möglich.

using System;
using System.Reflection;

class ClassA {
  public byte b;
  public short s; 
  public int i;
}

class ClassB {
  public long l;
  public ClassA x;
}

class MainClass {

  public static void Main() {
    ClassB myBObject = new ClassB();
    WriteFields(myBObject.GetType(), 0);
  }

  static void WriteFields(Type type, Int32 indent) {
    foreach (FieldInfo fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) {
      Console.WriteLine("{0}{1}\t{2}", new String('\t', indent), fieldInfo.FieldType.Name, fieldInfo.Name);
      if (fieldInfo.FieldType.IsClass)
        WriteFields(fieldInfo.FieldType, indent + 1);
    }
  }

}

6voto

Tony Heupel Punkte 1033

Die Klasse, die dies tut, existiert bereits! Werfen Sie einen Blick auf die Microsoft C# Samples für Visual Studio: http://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=csharpsamples&ReleaseId=8

Schauen Sie sich insbesondere das ObjectDumper-Beispiel an, das n-Ebenen tief geht. Zum Beispiel:

ClassB myBObject = new ClassB();
...
ObjectDumper.Write(myBObject, Int32.MaxValue); 
//Default 3rd argument value is Console.Out, but you can use 
//any TextWriter as the optional third argument

Es hat bereits berücksichtigt, ob ein Objekt im Graphen besucht wurde, Werttypen vs. Objekttypen vs. aufzählbare Typen, etc.

1voto

LBushkin Punkte 124894

Versuchen Sie das Folgende. Damit können Sie kontrollieren, wie tief Sie in die Typenhierarchie hinabsteigen und sollten nur in nicht-primitive Typen hinabsteigen.

public static class FieldExtensions
{
  public static IEnumerable<FieldInfo> GetFields( this Type type, int depth )
  {
    if( depth == 0 )
      return Enumerable.Empty<FieldInfo>();

    FieldInfo[] fields = type.GetFields();
    return fields.Union(fields.Where( fi => !fi.IsPrimitive )
                              .SelectMany( f => f.FieldType.GetFields( depth -1 ) );
  }
}

0voto

SLaks Punkte 832502

Sie müssen eine rekursive Methode schreiben, die ein Objekt aufnimmt und in einer Schleife durch seine Felder ( obj.GetType().GetFields() ), und gibt den Wert eines Feldes vom primitiven Typ aus, und ruft sich selbst für eine Klasse (anders als String ).

Sie benötigen einen Parameter für die Größe des Einzugs für die Verwendung mit Rekursion.

EDITAR : Sie brauchen auch einen Mechanismus, um einen Stapelüberlauf bei zyklischen Objektgraphen zu verhindern. Ich empfehle eine Begrenzung des Einrückungsparameters.

0voto

JoshBerke Punkte 64214

Hier ist eine naive Umsetzung:

    private static void ListFields(Type type)
    {
        Console.WriteLine(type.Name);
        foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
        {
            Console.WriteLine(string.Format("{0} of type {1}", field.Name, field.FieldType.Name));
            if (field.FieldType.IsClass)
            {
                ListFields(field.FieldType);
            }

        }
    }

Einige Dinge sind zu beachten:

  • Verhindern Sie einen Stapelüberlauf. Das heißt, wenn a -> b und b-> a, dann wird dies explodieren. Sie können das Problem lösen, indem Sie nur bis zu einer bestimmten Ebene auflösen
  • Eine Zeichenkette ist ein Referenztyp, aber viele Leute erwarten, dass sie eher wie ein Wertetyp ist. Daher möchten Sie ListFields vielleicht nicht aufrufen, wenn der Typ String ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X