In .NET & C#, suppose ClassB
has a field that is of type ClassA
.
One can easily use method GetFields
to list ClassB
\'
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.
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 ) );
}
}
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);
}
}
}
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:
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.