将整个对象转储到C#中的日志的最佳方法是什么?
因此,为了在运行时查看当前对象的状态,我真的很喜欢Visual Studio“立即”窗口给我的东西。 只是做一个简单的
? objectname
将给我一个很好格式化的对象的“转储”。
有没有简单的方法来做到这一点在代码中,所以我可以做类似的日志logging?
您可以基于Linq示例附带的ObjectDumper代码。
也看看这个相关问题的答案来得到一个样本。
对于一个更大的对象图,我第二次使用了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);
lookHere会自动显示在Locals
窗口前加$或你可以添加一个手表。 在检查员的Value
栏的右边,有放大镜旁边有一个下拉字符。 select下拉插入符号并selectJson可视化器。
我正在使用Visual Studio 2013。
我敢肯定有这样做的更好的方法,但我在过去使用类似于下面的方法将对象序列化成我可以logging的string:
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; }
你会发现这个方法可能会返回exception,而不是序列化的对象,所以你需要确保你想logging的对象是可序列化的。
我有一个T.Dump()扩展方法 ,完全是这样的,recursion地转储任何types的所有属性的可读格式。
用法示例:
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 } }
您可以使用Visual Studio立即窗口
只需粘贴(明显改变你的对象名称):
Newtonsoft.Json.JsonConvert.SerializeObject(actual);
它应该在JSON中打印对象
您应该能够通过textmechanic文本工具或记事本++复制它,并用空格和换行符( \r\n
)replace转义引号( \"
),然后从开始和结束删除双引号( "
),并将其粘贴让我们更加可读。
更新OP的评论
public static class Dumper { public static void Dump(this object obj) { Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(obj)); // your logger } }
这应该允许你转储任何对象。
希望这可以为你节省一些时间。
这是一个愚蠢的简单的方法来写一个平面对象,很好地格式化:
using Newtonsoft.Json.Linq; Debug.WriteLine("The object is " + JObject.FromObject(theObjectToDump).ToString());
发生什么事是对象JObject.FromObject
转换成JSON内部表示,然后再由ToString
转换成JSONstring。 (当然JSONstring是一个简单的对象的一个很好的表示,特别是因为ToString
将包含换行符和缩进。)“ToString”当然是无关的(因为它使用+
来表示string和对象) ,但我有点喜欢在这里指定它。
您可以使用reflection并遍历所有对象属性,然后获取它们的值并将其保存到日志中。 格式化非常简单(您可以使用\ t缩进对象属性及其值):
MyObject Property1 = value Property2 = value2 OtherObject OtherProperty = value ...
我喜欢做的是重写ToString(),以便获得更多types名称以外的有用输出。 这在debugging器中很方便,您可以在不需要扩展的情况下查看有关对象的信息。
我find了一个名为ObjectPrinter的库,它允许轻松地将对象和集合转储到string(以及更多)。 它正是我所需要的。
你可以写你自己的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
和检查接口,属性,基types等等和recursion(因为这是一个recursion方法),这样我们可以实现一个对象倾销,但确实很乏味。 使用微软LINQ Sample的对象翻转器可以节省您的时间。
以下是做同样的事情(和处理嵌套属性)的另一个版本,我认为这是更简单(没有依赖外部库,可以很容易地修改,而不是logging):
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")); } }