使用C#和反射打印完整对象图

时间:2009-09-18 09:52:31

标签: c# reflection

我有一个compex对象

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

}

我想使用反射打印完整的对象图。那里有什么好的代码?

3 个答案:

答案 0 :(得分:8)

一种极简主义的替代方案,能够以可读格式显示复杂对象:

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 + "???";
    }
}

答案 1 :(得分:6)

我有点像我的,因为它编译。我已经将它实现为一系列扩展方法和静态内容,因此将它放在项目中的某个静态类中,一旦包含包含静态类的命名空间,扩展方法将立即可用。以下是您使用它的方式:

myObject.PrintGraph();

它将递归地一直向下遍历你的图形,直到它找到可以转换为Convert.ToString()的东西,然后它将Debug.Print输出到你的即时窗口。以下是一些示例输出:

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

它只打印公共属性。

这是PrintGraph方法:

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

以下是让它发挥作用所需的基础设施:

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

答案 2 :(得分:2)

几年前我做了一些调试工作。它是一个递归函数,可以打印所有属性和子对象。您打印的方式取决于您。只需将您想要的代码放在print方法中即可。它不是“防弹”,但效果很好:

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

希望有所帮助