Reputation: 2575
In .NET & C#, suppose ClassB
has a field that is of type ClassA
.
One can easily use method GetFields
to list ClassB
's fields.
However, I want to also list the fields of those ClassB
fields that themselves have fields.
For example, ClassB
's field x
has fields b
, s
, and i
. I'd like to (programmatically) list those fields (as suggested by my comments in the code below).
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
}
}
Upvotes: 3
Views: 8932
Reputation: 106826
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);
}
}
}
Upvotes: 8
Reputation: 131676
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 ) );
}
}
Upvotes: 1
Reputation: 1053
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.
Upvotes: 6
Reputation: 67068
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:
Upvotes: 0
Reputation: 887453
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.
Upvotes: 0