.NET,C#,Reflection:列出字段的字段,字段本身具有字段

时间:2009-07-13 17:12:35

标签: c# .net reflection recursion

在.NET& C#,假设ClassB的字段类型为ClassA。 可以轻松使用方法GetFields列出ClassB的字段。 但是,我希望列出自己包含字段的ClassB字段的字段。 例如,ClassB的字段x包含字段bsi。我想(以编程方式)列出这些字段(正如我在以下代码中的评论所示)。

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 个答案:

答案 0 :(得分:7)

使用FieldInfo.FieldType反映班级中字段的类型。 E.g。

fieldInfo.FieldType.GetFields();

以下是基于您的代码的完整示例,如果您在ClassZClassA,则会使用递归。如果你有一个循环对象图,它就会中断。

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

}

答案 1 :(得分:6)

执行此操作的类已经存在!看一下Visual Studio的Microsoft C#示例: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

它已经考虑了图表中的对象是否已被访问,值类型与对象类型与可枚举类型等等。

答案 2 :(得分: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 ) );
  }
}

答案 3 :(得分:0)

您需要编写一个递归方法,该方法接受一个对象,遍历其字段(obj.GetType().GetFields()),并打印基本类型字段的值,并为一个类调用自身({{1除外) }})。

缩进大小需要一个参数用于递归。

编辑:您还需要一些机制来防止循环对象图的堆栈溢出。我建议对缩进参数设置限制。

答案 4 :(得分: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);
            }

        }
    }

有些注意事项:

  • 防止堆栈溢出。那就是a - &gt; b和b-&gt;那么这会爆炸。您可以通过仅解析到某个级别来解决此问题
  • 字符串是一种引用类型,但很多人都希望它更像是一种值类型。因此,如果类型为字符串,您可能不想调用ListField。