代码之家  ›  专栏  ›  技术社区  ›  Dan Esparza

将整个对象转储到C中的日志的最佳方法是什么?

  •  103
  • Dan Esparza  · 技术社区  · 16 年前

    因此,对于在运行时查看当前对象的状态,我真的很喜欢Visual Studio即时窗口提供的功能。只是做一个简单的

    ? objectname
    

    将给我一个格式良好的对象“转储”。

    在代码中是否有一种简单的方法可以做到这一点,以便在日志记录时做类似的事情?

    12 回复  |  直到 6 年前
        1
  •  51
  •   Community CDub    7 年前

    您可以基于随附的ObjectDumper代码 Linq samples .
    再看看这个问题的答案 related question 获取样本。

        2
  •  41
  •   Jason    10 年前

    对于一个更大的对象图,我第二次使用JSON,但使用的策略略有不同。首先,我有一个易于调用的静态类,并且使用一个包装JSON转换的静态方法(注意:可以将其作为扩展方法)。

    using Newtonsoft.Json;
    
    public static class F
    {
        public static string Dump(object obj)
        {
            return JsonConvert.SerializeObject(obj);
        }
    }
    

    然后在你 Immediate Window ,

    var lookHere = F.Dump(myobj);
    

    看这里会自动出现在 Locals 窗口前面有一块钱,或者你可以在上面加一块表。在 Value 在Inspector的列中,有一个放大镜,旁边有一个下拉插入符号。选择下拉插入符号并选择JSON可视化工具。

    Screenshot of Visual Studio 2013 Locals window

    我正在使用Visual Studio 2013。

        3
  •  24
  •   Bernhard Hofmann    16 年前

    我确信有更好的方法可以做到这一点,但在过去,我曾使用类似以下的方法将对象序列化为我可以记录的字符串:

      private string ObjectToXml(object output)
      {
         string objectAsXmlString;
    
         System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(output.GetType());
         using (System.IO.StringWriter sw = new System.IO.StringWriter())
         {
            try
            {
               xs.Serialize(sw, output);
               objectAsXmlString = sw.ToString();
            }
            catch (Exception ex)
            {
               objectAsXmlString = ex.ToString();
            }
         }
    
         return objectAsXmlString;
      }
    

    您将看到该方法还可能返回异常而不是序列化对象,因此您需要确保要记录的对象是可序列化的。

        4
  •  17
  •   mythz    13 年前

    我有一个 T.Dump() extension method 这样做,递归地以一种可读性好的格式转储任何类型的所有属性。

    示例用法:

    var model = new TestModel();
    Console.WriteLine(model.Dump());
    

    输出:

    {
        Int: 1,
        String: One,
        DateTime: 2010-04-11,
        Guid: c050437f6fcd46be9b2d0806a0860b3e,
        EmptyIntList: [],
        IntList:
        [
            1,
            2,
            3
        ],
        StringList:
        [
            one,
            two,
            three
        ],
        StringIntMap:
        {
            a: 1,
            b: 2,
            c: 3
        }
    }
    
        5
  •  14
  •   Matas Vaitkevicius user3782709    9 年前

    您可以使用Visual Studio即时窗口

    只需粘贴这个(更改 actual 显然,对象名是:

    Newtonsoft.Json.JsonConvert.SerializeObject(actual);
    

    它应该在JSON中打印对象 enter image description here

    你应该可以复制它 over textmechanic text tool notepad++ 并替换转义引号( \" " 换行符(新行) \r\n )带空格,然后删除双引号( )从开始到结束粘贴到 jsbeautifier 使其更具可读性。

    更新至OP的评论

    public static class Dumper
    {
        public static void Dump(this object obj)
        {
            Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(obj)); // your logger
        }
    }
    

    这将允许您转储任何对象。

    希望这能节省你一些时间。

        6
  •  10
  •   Hot Licks    10 年前

    下面是一种简单的方法,可以编写一个格式良好的平面对象:

    using Newtonsoft.Json.Linq;
    
    Debug.WriteLine("The object is " + JObject.FromObject(theObjectToDump).ToString());
    

    发生的事情是,首先通过以下方式将对象转换为JSON内部表示: JObject.FromObject ,然后由转换为JSON字符串 ToString . (当然,JSON字符串是一个简单对象的非常好的表示,特别是在 弦线 将包括换行和缩进。)“to字符串”当然是无关的(正如使用 + 连接一个字符串和一个对象),但我有点想在这里指定它。

        7
  •  4
  •   Ricardo Villamil    16 年前

    您可以使用反射并遍历所有对象属性,然后获取它们的值并将它们保存到日志中。格式化非常简单(可以使用\t缩进对象属性及其值):

    MyObject
        Property1 = value
        Property2 = value2
        OtherObject
           OtherProperty = value ...
    
        8
  •  3
  •   Darryl Braaten    16 年前

    我喜欢做的是重写toString(),以便在类型名之外获得更有用的输出。这在调试器中很方便,您可以看到您想要的关于对象的信息,而无需展开它。

        9
  •  3
  •   Marek Dzikiewicz    9 年前

    我找到一个图书馆 ObjectPrinter 它允许轻松地将对象和集合转储到字符串(以及更多)。它完全符合我的需要。

        10
  •  3
  •   engineforce    8 年前

    下面是另一个执行相同操作(并处理嵌套属性)的版本,我认为这更简单(不依赖于外部库,可以轻松修改以执行日志以外的操作):

    public class ObjectDumper
    {
        public static string Dump(object obj)
        {
            return new ObjectDumper().DumpObject(obj);
        }
    
        StringBuilder _dumpBuilder = new StringBuilder();
    
        string DumpObject(object obj)
        {
            DumpObject(obj, 0);
            return _dumpBuilder.ToString();
        }
    
        void DumpObject(object obj, int nestingLevel = 0)
        {
            var nestingSpaces = "".PadLeft(nestingLevel * 4);
    
            if (obj == null)
            {
                _dumpBuilder.AppendFormat("{0}null\n", nestingSpaces);
            }
            else if (obj is string || obj.GetType().IsPrimitive)
            {
                _dumpBuilder.AppendFormat("{0}{1}\n", nestingSpaces, obj);
            }
            else if (ImplementsDictionary(obj.GetType()))
            {
                using (var e = ((dynamic)obj).GetEnumerator())
                {
                    var enumerator = (IEnumerator)e;
                    while (enumerator.MoveNext())
                    {
                        dynamic p = enumerator.Current;
    
                        var key = p.Key;
                        var value = p.Value;
                        _dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, key, value != null ? value.GetType().ToString() : "<null>");
                        DumpObject(value, nestingLevel + 1);
                    }
                }
            }
            else if (obj is IEnumerable)
            {
                foreach (dynamic p in obj as IEnumerable)
                {
                    DumpObject(p, nestingLevel);
                }
            }
            else
            {
                foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj))
                {
                    string name = descriptor.Name;
                    object value = descriptor.GetValue(obj);
    
                    _dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, name, value != null ? value.GetType().ToString() : "<null>");
                    DumpObject(value, nestingLevel + 1);
                }
            }
        }
    
        bool ImplementsDictionary(Type t)
        {
            return t.GetInterfaces().Any(i => i.Name.Contains("IDictionary"));
        }
    }
    
        11
  •  2
  •   Ariful Islam    9 年前

    您可以编写自己的writeline方法-

    public static void WriteLine<T>(T obj)
        {
            var t = typeof(T);
            var props = t.GetProperties();
            StringBuilder sb = new StringBuilder();
            foreach (var item in props)
            {
                sb.Append($"{item.Name}:{item.GetValue(obj,null)}; ");
            }
            sb.AppendLine();
            Console.WriteLine(sb.ToString());
        }
    

    像这样使用它

    WriteLine(myObject);
    

    要编写一个集合,我们可以使用-

     var ifaces = t.GetInterfaces();
            if (ifaces.Any(o => o.Name.StartsWith("ICollection")))
            {
    
                dynamic lst = t.GetMethod("GetEnumerator").Invoke(obj, null);
                while (lst.MoveNext())
                {
                    WriteLine(lst.Current);
                }
            }   
    

    方法可能看起来像-

     public static void WriteLine<T>(T obj)
        {
            var t = typeof(T);
            var ifaces = t.GetInterfaces();
            if (ifaces.Any(o => o.Name.StartsWith("ICollection")))
            {
    
                dynamic lst = t.GetMethod("GetEnumerator").Invoke(obj, null);
                while (lst.MoveNext())
                {
                    WriteLine(lst.Current);
                }
            }            
            else if (t.GetProperties().Any())
            {
                var props = t.GetProperties();
                StringBuilder sb = new StringBuilder();
                foreach (var item in props)
                {
                    sb.Append($"{item.Name}:{item.GetValue(obj, null)}; ");
                }
                sb.AppendLine();
                Console.WriteLine(sb.ToString());
            }
        }
    

    使用 if, else if 通过这种方式检查接口、属性、基类型等和递归(因为这是一种递归方法),我们可以实现一个对象转储程序,但这确实很麻烦。使用来自Microsoft Linq示例的对象转储程序可以节省您的时间。

        12
  •  0
  •   gianlucaparadise    6 年前

    基于@engineforce answer,我在Xamarin解决方案的PCL项目中使用了这个类:

    /// <summary>
    /// Based on: https://stackoverflow.com/a/42264037/6155481
    /// </summary>
    public class ObjectDumper
    {
        public static string Dump(object obj)
        {
            return new ObjectDumper().DumpObject(obj);
        }
    
        StringBuilder _dumpBuilder = new StringBuilder();
    
        string DumpObject(object obj)
        {
            DumpObject(obj, 0);
            return _dumpBuilder.ToString();
        }
    
        void DumpObject(object obj, int nestingLevel)
        {
            var nestingSpaces = "".PadLeft(nestingLevel * 4);
    
            if (obj == null)
            {
                _dumpBuilder.AppendFormat("{0}null\n", nestingSpaces);
            }
            else if (obj is string || obj.GetType().GetTypeInfo().IsPrimitive || obj.GetType().GetTypeInfo().IsEnum)
            {
                _dumpBuilder.AppendFormat("{0}{1}\n", nestingSpaces, obj);
            }
            else if (ImplementsDictionary(obj.GetType()))
            {
                using (var e = ((dynamic)obj).GetEnumerator())
                {
                    var enumerator = (IEnumerator)e;
                    while (enumerator.MoveNext())
                    {
                        dynamic p = enumerator.Current;
    
                        var key = p.Key;
                        var value = p.Value;
                        _dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, key, value != null ? value.GetType().ToString() : "<null>");
                        DumpObject(value, nestingLevel + 1);
                    }
                }
            }
            else if (obj is IEnumerable)
            {
                foreach (dynamic p in obj as IEnumerable)
                {
                    DumpObject(p, nestingLevel);
                }
            }
            else
            {
                foreach (PropertyInfo descriptor in obj.GetType().GetRuntimeProperties())
                {
                    string name = descriptor.Name;
                    object value = descriptor.GetValue(obj);
    
                    _dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, name, value != null ? value.GetType().ToString() : "<null>");
    
                    // TODO: Prevent recursion due to circular reference
                    if (name == "Self" && HasBaseType(obj.GetType(), "NSObject"))
                    {
                        // In ObjC I need to break the recursion when I find the Self property
                        // otherwise it will be an infinite recursion
                        Console.WriteLine($"Found Self! {obj.GetType()}");
                    }
                    else
                    {
                        DumpObject(value, nestingLevel + 1);
                    }
                }
            }
        }
    
        bool HasBaseType(Type type, string baseTypeName)
        {
            if (type == null) return false;
    
            string typeName = type.Name;
    
            if (baseTypeName == typeName) return true;
    
            return HasBaseType(type.GetTypeInfo().BaseType, baseTypeName);
        }
    
        bool ImplementsDictionary(Type t)
        {
            return t is IDictionary;
        }
    }