因此,对于在运行时查看当前对象的状态,我真的很喜欢 Visual Studio 即时窗口给我的东西。只是做一个简单的
? objectname
会给我一个格式很好的对象“转储”。
有没有一种简单的方法可以在代码中做到这一点,所以我可以在记录时做类似的事情?
因此,对于在运行时查看当前对象的状态,我真的很喜欢 Visual Studio 即时窗口给我的东西。只是做一个简单的
? objectname
会给我一个格式很好的对象“转储”。
有没有一种简单的方法可以在代码中做到这一点,所以我可以在记录时做类似的事情?
对于更大的对象图,我第二次使用 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
,有一个放大镜,旁边有一个下拉插入符号。选择下拉插入符号并选择 Json 可视化工具。
我正在使用 Visual Studio 2013。
您可以基于Linq 示例附带的 ObjectDumper 代码。
还请查看此相关问题的答案以获取示例。
您可以使用 Visual Studio 即时窗口
只需粘贴这个(actual
显然更改为您的对象名称):
Newtonsoft.Json.JsonConvert.SerializeObject(actual);
它应该以 JSON 格式打印对象
您应该能够将其复制到 textmechanic 文本工具或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
}
}
这应该允许您转储任何对象。
希望这可以节省您一些时间。
我确信有更好的方法可以做到这一点,但我过去曾使用类似以下的方法将对象序列化为我可以记录的字符串:
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;
}
您会看到该方法也可能返回异常而不是序列化对象,因此您需要确保要记录的对象是可序列化的。
ServiceStack.Text有一个T.Dump() 扩展方法可以做到这一点,递归地以一种可读的格式递归地转储任何类型的所有属性。
示例用法:
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
}
}
这是编写平面对象的一种非常简单的方法,格式很好:
using Newtonsoft.Json.Linq;
Debug.WriteLine("The object is " + JObject.FromObject(theObjectToDump).ToString());
发生的事情是对象首先通过 转换为 JSON 内部表示JObject.FromObject
,然后通过 转换为 JSON 字符串ToString
。(当然,JSON 字符串是简单对象的一种非常好的表示形式,尤其是因为ToString
将包含换行符和缩进。)“ToString”当然是无关紧要的(因为它暗示了+
用于连接字符串和对象),但是我有点想在这里指定它。
您可以使用反射并循环遍历所有对象属性,然后获取它们的值并将它们保存到日志中。格式非常简单(您可以使用 \t 缩进对象属性及其值):
MyObject
Property1 = value
Property2 = value2
OtherObject
OtherProperty = value ...
我喜欢做的是覆盖 ToString() 以便我获得超出类型名称的更有用的输出。这在调试器中很方便,您可以查看您想要的有关对象的信息,而无需扩展它。
以下是另一个执行相同操作(并处理嵌套属性)的版本,我认为它更简单(不依赖于外部库,并且可以轻松修改以执行日志记录以外的操作):
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"));
}
}
我找到了一个名为ObjectPrinter的库,它可以轻松地将对象和集合转储到字符串(以及更多)。它完全符合我的需要。
您可以编写自己的 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 示例中的对象转储程序可以节省您的时间。
基于@engineforce 的回答,我制作了我在 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;
}
}
到目前为止,对我来说最简单和最整洁的方法是使用 package.json 的序列化器YamlDotNet
。
using YamlDotNet.Serialization;
List<string> strings=new List<string>{"a","b","c"};
new Serializer().Serialize(strings)
会给你
- a
- b
- c
一个更全面的例子在这里https://dotnetfiddle.net/KuV63n
上述所有路径都假定您的对象可序列化为 XML 或 JSON,
或者您必须实现自己的解决方案。
但最终你仍然需要解决诸如
加日志您想了解更多信息:
有最好的解决方案可以解决所有这些问题以及更多问题。
使用这个 Nuget 包:Desharp。
适用于所有类型的应用程序 - Web 和桌面应用程序。
请参阅它的Desharp Github 文档。它有许多配置选项。
随便打电话:
Desharp.Debug.Log(anyException);
Desharp.Debug.Log(anyCustomValueObject);
Desharp.Debug.Log(anyNonserializableObject);
Desharp.Debug.Log(anyFunc);
Desharp.Debug.Log(anyFunc, Desharp.Level.EMERGENCY); // you can store into different files
我相信它会有所帮助。