Вопрос по recursion, .net, reflection, c# – .NET, C #, Reflection: перечислить поля поля, которое само имеет поля

3

В .NET & amp; C #, предположимClassB имеет поле, которое имеет типClassA. One can easily use method GetFields к спискуClassBполя. Тем не менее, я хочуalso перечислите поля техClassB поля, которыеthemselves есть поля. Например,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

        }
    }
Это кандидат на какой-то грубый LINQ? :) xyz

Ваш Ответ

5   ответов
0

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

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

EDITВам также понадобится некоторый механизм для предотвращения переполнения стека для циклических графов объектов. Я рекомендую установить ограничение на параметр indent.

6

Класс, который делает это, уже существует! Взгляните на примеры 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

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

7

Использовать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);
    }
  }

}
Вот Это Да! Вы, ребята, были очень полезны! Это первое предложение, которое я получил, но другие предложения тоже очень хороши! Спасибо всем! JaysonFix
+1 за классное использование отступа: D
& quot; if (fieldInfo.FieldType.IsClass) & quot; не совсем то, что вы хотите. Это будет включать "строку" (что вы не хотите), и это исключило бы определяемые пользователем структуры (которые вы хотите). Возможно, вы могли бы просто сказать «if (! FieldInfo.FieldType.Namespace.Equals (& quot; System & quot;))».
Это определенно работает для вашего случая. Если вам необходимо продолжить это в будущем с более сложными объектами или если что-то не работает должным образом, рассмотрите мой ответ. Он был написан командой C #, и вы можете взять и настроить код по своему усмотрению, но он гибкий как есть. И добро пожаловать ... рад, что все это помогло! :-)
0

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

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

        }
    }

Некоторые вещи, на которые стоит обратить внимание:

  • Prevent a stack overflow. That is if a -> b and b-> a then this will blow up. You can resolve this by only resolving down to a certain level
  • A string is a reference type but lots of people expect it to be more like a value type. So you might not want to call ListFields if the type is string.
1

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

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

Похожие вопросы