.NET, C #, Reflection: перечислить поля поля, которое само имеет поля - PullRequest
3 голосов
/ 13 июля 2009

В .NET & C # предположим, что ClassB имеет поле типа ClassA. Можно легко использовать метод GetFields для перечисления полей ClassB. Однако я хочу также перечислить поля тех полей ClassB, которые сами имеют поля. Например, поле ClassB x имеет поля b, s и i. Я хотел бы (программно) перечислить эти поля (как это предусмотрено моими комментариями в приведенном ниже коде).

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

        }
    }

Ответы [ 5 ]

7 голосов
/ 13 июля 2009

Используйте FieldInfo.FieldType, чтобы отразить тип полей в вашем классе. Э.Г.

fieldInfo.FieldType.GetFields();

Вот полный пример, основанный на вашем коде, который использует рекурсию, если у вас есть ClassZ внутри ClassA. Он ломается, если у вас есть циклический граф объектов.

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

}
6 голосов
/ 13 июля 2009

Класс, который делает это, уже существует! Взгляните на примеры Microsoft C # для Visual Studio: http://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=csharpsamples&ReleaseId=8

В частности, посмотрите на образец ObjectDumper, так как он углубляется на n уровней. Например:

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

Уже учтено, был ли посещен объект на графике, типы значений по сравнению с типами объектов по сравнению с перечисляемыми типами и т. Д.

1 голос
/ 13 июля 2009

Попробуйте следующее. Это позволяет вам контролировать, насколько глубоко вы спускаетесь в иерархию типов и должны опускаться только в непримитивные типы.

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 ) );
  }
}
0 голосов
/ 13 июля 2009

Вот наивная реализация:

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

        }
    }

Некоторые вещи на заметку:

  • Предотвращение переполнения стека. То есть если a -> b и b-> a, то это взорвется. Вы можете решить эту проблему, разрешив только до определенного уровня
  • Строка является ссылочным типом, но многие ожидают, что он будет больше похож на тип значения. Поэтому вы можете не вызывать ListFields, если тип является строковым.
0 голосов
/ 13 июля 2009

Вам необходимо написать рекурсивный метод, который принимает объект, проходит по его полям (obj.GetType().GetFields()) и печатает значение поля примитивного типа и вызывает себя для класса (отличного от String).

Вам потребуется параметр для размера отступа для использования с рекурсией.

РЕДАКТИРОВАТЬ : Вам также понадобится некоторый механизм для предотвращения переполнения стека для циклических графов объектов. Я рекомендую установить ограничение на параметр indent.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...