¿Cuál es la mejor manera de volcar objetos completos a un registro en C #?

Entonces, para ver el estado de un objeto actual en tiempo de ejecución, realmente me gusta lo que la ventana de Visual Studio Inmediato me ofrece. Simplemente haciendo un simple

? objectname 

Me dará un ‘volcado’ muy bien formateado del objeto.

¿Existe alguna manera fácil de hacer esto en el código, así que puedo hacer algo similar al iniciar sesión?

Podría basar algo en el código ObjectDumper que se envía con las muestras de Linq .
También eche un vistazo a la respuesta de esta pregunta relacionada para obtener una muestra.

Para un gráfico de objeto más grande, secundo el uso de Json pero con una estrategia ligeramente diferente. Primero tengo una clase estática que es fácil de llamar y con un método estático que envuelve la conversión Json (nota: podría hacer que este sea un método de extensión).

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

Luego, en tu Immediate Window ,

 var lookHere = F.Dump(myobj); 

lookHere se mostrará automáticamente en la ventana Locals precedida de $ o puede agregarle un reloj. En el lado derecho de la columna Value en el inspector, hay una lupa con un cursor desplegable al lado. Elija el cursor desplegable y elija el visualizador Json.

Captura de pantalla de la ventana de Locals de Visual Studio 2013

Estoy usando Visual Studio 2013.

Estoy seguro de que hay mejores formas de hacerlo, pero en el pasado usé un método como el siguiente para serializar un objeto en una cadena que puedo registrar:

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

Verá que el método también puede devolver la excepción en lugar del objeto serializado, por lo que querrá asegurarse de que los objetos que desea registrar sean serializables.

Tengo un método de extensión T.Dump () que hace exactamente esto, vuelca recursivamente todas las propiedades de cualquier tipo en un buen formato legible.

Ejemplo de uso:

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

y salida:

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

Podría usar la Ventana Inmediata de Visual Studio

Simplemente pegue esto (cambie el nombre actual de su objeto obviamente):

 Newtonsoft.Json.JsonConvert.SerializeObject(actual); 

Debería imprimir el objeto en JSON enter image description here

Debería poder copiarlo sobre la herramienta de texto textmechanic o notepad ++ y reemplazar las comillas escapadas ( \" ) con " y nuevas líneas ( \r\n ) con espacio vacío, luego quitar las comillas dobles ( " ) del principio y el final y pegarlas a jsbeautifier para hacerlo más legible.

ACTUALIZAR al comentario de OP

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

esto debería permitirle volcar cualquier objeto.

Espero que esto te ahorre algo de tiempo.

Aquí hay una forma estúpidamente simple de escribir un objeto plano, muy bien formateado:

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

Lo que sucede es que el objeto se convierte primero a una representación interna JSON por JObject.FromObject , y luego se convierte en cadena JSON por ToString . (Y, por supuesto, una cadena JSON es una muy buena representación de un objeto simple, especialmente porque ToString incluirá líneas nuevas y sangrías). El “ToString” es por supuesto extraño (como se implica al usar + para concaturar una cadena y un objeto) , pero me gustaría especificarlo aquí.

Podría usar la reflexión y recorrer todas las propiedades del objeto, luego obtener sus valores y guardarlos en el registro. El formateo es realmente trivial (puede usar \ t para sangrar las propiedades de un objeto y sus valores):

 MyObject Property1 = value Property2 = value2 OtherObject OtherProperty = value ... 

Lo que me gusta es anular ToString () para que obtenga un resultado más útil más allá del nombre de tipo. Esto es útil en el depurador, puede ver la información que desea sobre un objeto sin necesidad de expandirlo.

Encontré una biblioteca llamada ObjectPrinter que permite volcar fácilmente objetos y colecciones en cadenas (y más). Hace exactamente lo que necesitaba.

Puedes escribir tu propio método WriteLine-

 public static void WriteLine(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()); } 

Úselo como-

 WriteLine(myObject); 

Para escribir una colección, podemos usar-

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

El método puede parecerse a …

  public static void WriteLine(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()); } } 

Usando if, else if y revisando interfaces, atributos, tipo de base, etc. y recursión (ya que este es un método recursivo) de esta manera podemos lograr un volcado de objetos, pero es tedioso con seguridad. Usar el volcador de objetos de LINQ Sample de Microsoft le ahorraría tiempo.

A continuación se muestra otra versión que hace lo mismo (y maneja propiedades anidadas), que creo que es más simple (sin dependencias en bibliotecas externas y se puede modificar fácilmente para hacer otras cosas además del registro):

 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() : ""); 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() : ""); DumpObject(value, nestingLevel + 1); } } } bool ImplementsDictionary(Type t) { return t.GetInterfaces().Any(i => i.Name.Contains("IDictionary")); } }