.NET, C#, Reflection: list the fields of a field that, itself, has fields

前端 未结 5 1773
轻奢々
轻奢々 2020-12-11 08:53

In .NET & C#, suppose ClassB has a field that is of type ClassA. One can easily use method GetFields to list ClassB\'

相关标签:
5条回答
  • 2020-12-11 09:01

    The class that does this already exists! Take a look at the Microsoft C# Samples for Visual Studio: http://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=csharpsamples&ReleaseId=8

    Specifically, look at the ObjectDumper sample as it goes n-levels deep. For example:

    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
    

    It has already taken into account whether an object in the graph has been visited, Value types vs. object types vs. enumerable types, etc.

    0 讨论(0)
  • 2020-12-11 09:08

    Try the following. It lets you control how deep you descend into the type hierarchy and should only descend into non-primitive types.

    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 讨论(0)
  • 2020-12-11 09:20

    Use the FieldInfo.FieldType to reflect over the type of the fields in your class. E.g.

    fieldInfo.FieldType.GetFields();
    

    Here is a complete sample based on your code that uses recursion in case you have ClassZ inside ClassA. It breaks if you have a cyclic object graph.

    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);
        }
      }
    
    }
    
    0 讨论(0)
  • 2020-12-11 09:20

    Here's a naive implementation:

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

    Some things to note:

    • 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.
    0 讨论(0)
  • 2020-12-11 09:24

    You need to write a recursive method that takes an object, loops through its fields (obj.GetType().GetFields()), and prints the value of a field of primitive type, and calls itself for a class (other than String).

    You'll need a parameter for the indent size for use with recursion.

    EDIT: You'll also need some mechanism to prevent a stack overflow for cyclic object graphs. I recommend placing a limit on the indent parameter.

    0 讨论(0)
提交回复
热议问题