Printing full object graph using C# and reflection

2020-06-06 04:42发布

问题:

I have a compex object

class A
{
 int Field1;
 int field2;
 property ClassB ClassB;
 property classC classC;
 etc etc....

}

I want to print the complete object graph using reflection. Any good code out there?

回答1:

I did something for debugging purpose some years ago. It's a recursive function that print all properties and sub object. The way you print is up to you. Just put the code you want in the print method. It's not "bullet proof" but it works pretty well :

private static void displayObject(object myObject, bool displaySubObject, Type objectType)
{
  print(objectType.FullName);
  if (myObject == null) 
  {
      print(STR_Null);
  }
  else 
  {
    //check for collection
    if (objectType.GetInterface("IEnumerable") != null) 
    {
      int itemNb = 0;
      foreach (object item in (IEnumerable)myObject) 
      {
        displayObject(item, displaySubObject, item.GetType);
        itemNb += 1;
      }
    }
    else 
    {
      ArrayList al = new ArrayList();
      Reflection.PropertyInfo pi = default(Reflection.PropertyInfo);
      Reflection.MemberInfo[] members = objectType.GetMembers();
      foreach (Reflection.MemberInfo mi in objectType.GetMembers()) 
      {
        if ((mi.MemberType & Reflection.MemberTypes.Constructor) != 0){//ignore constructor}
        else if (object.ReferenceEquals(mi.DeclaringType, typeof(object))) {//ignore inherited}
        else if (!al.Contains(mi.Name) & (mi.MemberType & Reflection.MemberTypes.Property) != 0) 
        {
          al.Add(mi.Name);
          pi = (Reflection.PropertyInfo)mi;
          if (!(displaySubObject) || (pi.PropertyType.IsValueType || pi.PropertyType.Equals(typeof(string)))) 
          {
            print(pi, myObject);
          }
          else 
          {
            //display sub objects
            displayObject(pi.GetValue(myObject, null), displaySubObject, i.PropertyType);
          }
        }
      }
    }
  }
}

Hope it helps



回答2:

A minimalist alternative, capable of displaying complex objects in a readable format:

public static string Dump(object o, string name = "", int depth = 3)
{
    try
    {
        var leafprefix = (string.IsNullOrWhiteSpace(name) ? name : name + " = ");

        if (null == o) return leafprefix + "null";

        var t = o.GetType();
        if (depth-- < 1 || t == typeof (string) || t.IsValueType)
            return  leafprefix + o;

        var sb = new StringBuilder();
        var enumerable = o as IEnumerable;
        if (enumerable != null)
        {
            name = (name??"").TrimEnd('[', ']') + '[';
            var elements = enumerable.Cast<object>().Select(e => Dump(e, "", depth)).ToList();
            var arrayInOneLine = elements.Count + "] = {" + string.Join(",", elements) + '}';
            if (!arrayInOneLine.Contains(Environment.NewLine)) // Single line?
                return name + arrayInOneLine;
            var i = 0;
            foreach (var element in elements)
            {
                var lineheader = name + i++ + ']';
                sb.Append(lineheader).AppendLine(element.Replace(Environment.NewLine, Environment.NewLine+lineheader));
            }
            return sb.ToString();
        }
        foreach (var f in t.GetFields())
            sb.AppendLine(Dump(f.GetValue(o), name + '.' + f.Name, depth));
        foreach (var p in t.GetProperties())
            sb.AppendLine(Dump(p.GetValue(o, null), name + '.' + p.Name, depth));
        if (sb.Length == 0) return leafprefix + o;
        return sb.ToString().TrimEnd();
    }
    catch
    {
        return name + "???";
    }
}


回答3:

I kinda like mine better, since it compiles. I've implemented it as a series of extension methods and static stuff, so put this in a static class somewhere in your project, and the extension methods will be instantly available once you include the namespace that contains the static class. Here's how you use it:

myObject.PrintGraph();

It will recursively tunnel all the way down through your graph until it finds something it can Convert.ToString(), which it will then Debug.Print out to your immediate window. Here is some sample output:

TopLevelProperty: value //Member of myObject
     MyEnumerableProperty: MyItemProperty: value //A property from an object in myObject
          MyEnumerableProperty: MySubEnumerableProperty: MyItemProperty: value //& so on

It only prints public properties, though.

Here's the PrintGraph method:

/// <summary>
/// Prints the graph of this object using Debug.Print.
/// </summary>
/// <param name="o">This object.</param>
/// <param name="prefix">Optional text to prepend to all lines printed by this method.
/// </param>
public static void PrintGraph(this object o, string prefix = "")
{
   Type t = o.GetType(); if (prefix != "") prefix = "     " + prefix;

   foreach (PropertyInfo p in t.GetProperties())
      if (p.PropertyType.IsConvertible()) Debug.Print(prefix + p.Name + ": " +
         Convert.ToString(p.GetValue(o, null)));
      else if (p.PropertyType.IsEnumerable())
         foreach (object sub in (IEnumerable)p.GetValue(o, null)) 
            PrintGraph(sub, prefix + p.Name + ": ");
      else if (p.SimpleGetter()) 
         PrintGraph(p.GetValue(o, null), prefix + p.Name + ": ");
   if (t.IsEnumerable()) foreach (object sub in (IEnumerable)o) PrintGraph(sub);
}

And here's the infrastructure you need to get it to work:

internal static Type[] ConvertibleTypes = {typeof(bool), typeof(byte), typeof(char),
   typeof(DateTime), typeof(decimal), typeof(double), typeof(float), typeof(int), 
   typeof(long), typeof(sbyte), typeof(short), typeof(string), typeof(uint), 
   typeof(ulong), typeof(ushort)};

/// <summary>
/// Returns true if this Type matches any of a set of Types.
/// </summary>
/// <param name="type">This type.</param>
/// <param name="types">The Types to compare this Type to.</param>
public static bool In(this Type type, params Type[] types) 
{ 
   foreach (Type t in types) if (t.IsAssignableFrom(type)) return true; return false; 
}

/// <summary>
/// Returns true if this Type is one of the types accepted by Convert.ToString() 
/// (other than object).
/// </summary>
public static bool IsConvertible(this Type t) { return t.In(ConvertibleTypes); }

/// <summary>
/// Gets whether this type is enumerable.
/// </summary>
public static bool IsEnumerable(this Type t) 
{ 
   return typeof(IEnumerable).IsAssignableFrom(t); 
}

/// <summary>
/// Returns true if this property's getter is public, has no arguments, and has no 
/// generic type parameters.
/// </summary>
public static bool SimpleGetter(this PropertyInfo info) 
{ 
   MethodInfo method = info.GetGetMethod(false); 
   return method != null && method.GetParameters().Length == 0 && 
      method.GetGenericArguments().Length == 0; 
}


标签: c# reflection