C #: Imprimir todas las propiedades de un objeto

¿Existe un método incorporado en .NET que pueda escribir todas las propiedades y demás de un objeto en la consola? Podría hacer uno usando la reflexión, por supuesto, pero tengo curiosidad por saber si esto ya existe … especialmente porque puedes hacerlo en Visual Studio en la ventana Inmediato. Allí puede un nombre de objeto (mientras está en modo de depuración), presione enter, y se imprime bastante bien con todas sus cosas.

¿Existe un método como este?

Se sabe que la clase ObjectDumper hace eso. Nunca lo confirmé, pero siempre sospeché que la ventana inmediata usa eso.

EDITAR: Me acabo de dar cuenta de que el código para ObjectDumper está realmente en su máquina. Ir:

c: / Archivos de progtwig / Microsoft Visual Studio 9.0 / Samples / 1033 / CSharpSamples.zip

Esto se descomprimirá en una carpeta llamada LinqSamples. Ahí, hay un proyecto llamado ObjectDumper. Usa eso.

(Esto también hará feliz a David en los comentarios :))

Puede usar la clase TypeDescriptor para hacer esto:

 foreach(PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj)) { string name=descriptor.Name; object value=descriptor.GetValue(obj); Console.WriteLine("{0}={1}",name,value); } 

TypeDescriptor vive en el espacio de nombres System.ComponentModel y es la API que Visual Studio usa para mostrar su objeto en su navegador de propiedades. En última instancia, se basa en la reflexión (como lo haría cualquier solución), pero proporciona un nivel bastante bueno de abstracción de la API de reflexión.

Basado en el ObjectDumper de los ejemplos de LINQ, creé una versión que volca cada una de las propiedades en su propia línea.

Esta muestra de clase

 namespace MyNamespace { public class User { public string FirstName { get; set; } public string LastName { get; set; } public Address Address { get; set; } public IList Hobbies { get; set; } } public class Hobby { public string Name { get; set; } } public class Address { public string Street { get; set; } public int ZipCode { get; set; } public string City { get; set; } } } 

tiene una salida de

 {MyNamespace.User} FirstName: "Arnold" LastName: "Schwarzenegger" Address: { } {MyNamespace.Address} Street: "6834 Hollywood Blvd" ZipCode: 90028 City: "Hollywood" Hobbies: ... {MyNamespace.Hobby} Name: "body building" 

Aquí está el código.

 using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Text; public class ObjectDumper { private int _level; private readonly int _indentSize; private readonly StringBuilder _stringBuilder; private readonly List _hashListOfFoundElements; private ObjectDumper(int indentSize) { _indentSize = indentSize; _stringBuilder = new StringBuilder(); _hashListOfFoundElements = new List(); } public static string Dump(object element) { return Dump(element, 2); } public static string Dump(object element, int indentSize) { var instance = new ObjectDumper(indentSize); return instance.DumpElement(element); } private string DumpElement(object element) { if (element == null || element is ValueType || element is string) { Write(FormatValue(element)); } else { var objectType = element.GetType(); if (!typeof(IEnumerable).IsAssignableFrom(objectType)) { Write("{{{0}}}", objectType.FullName); _hashListOfFoundElements.Add(element.GetHashCode()); _level++; } var enumerableElement = element as IEnumerable; if (enumerableElement != null) { foreach (object item in enumerableElement) { if (item is IEnumerable && !(item is string)) { _level++; DumpElement(item); _level--; } else { if (!AlreadyTouched(item)) DumpElement(item); else Write("{{{0}}} < -- bidirectional reference found", item.GetType().FullName); } } } else { MemberInfo[] members = element.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance); foreach (var memberInfo in members) { var fieldInfo = memberInfo as FieldInfo; var propertyInfo = memberInfo as PropertyInfo; if (fieldInfo == null && propertyInfo == null) continue; var type = fieldInfo != null ? fieldInfo.FieldType : propertyInfo.PropertyType; object value = fieldInfo != null ? fieldInfo.GetValue(element) : propertyInfo.GetValue(element, null); if (type.IsValueType || type == typeof(string)) { Write("{0}: {1}", memberInfo.Name, FormatValue(value)); } else { var isEnumerable = typeof(IEnumerable).IsAssignableFrom(type); Write("{0}: {1}", memberInfo.Name, isEnumerable ? "..." : "{ }"); var alreadyTouched = !isEnumerable && AlreadyTouched(value); _level++; if (!alreadyTouched) DumpElement(value); else Write("{{{0}}} <-- bidirectional reference found", value.GetType().FullName); _level--; } } } if (!typeof(IEnumerable).IsAssignableFrom(objectType)) { _level--; } } return _stringBuilder.ToString(); } private bool AlreadyTouched(object value) { if (value == null) return false; var hash = value.GetHashCode(); for (var i = 0; i < _hashListOfFoundElements.Count; i++) { if (_hashListOfFoundElements[i] == hash) return true; } return false; } private void Write(string value, params object[] args) { var space = new string(' ', _level * _indentSize); if (args != null) value = string.Format(value, args); _stringBuilder.AppendLine(space + value); } private string FormatValue(object o) { if (o == null) return ("null"); if (o is DateTime) return (((DateTime)o).ToShortDateString()); if (o is string) return string.Format("\"{0}\"", o); if (o is char && (char)o == '\0') return string.Empty; if (o is ValueType) return (o.ToString()); if (o is IEnumerable) return ("..."); return ("{ }"); } } 

y puedes usarlo así:

 var dump = ObjectDumper.Dump(user); 

Editar

  • Las referencias bidireccionales ahora están paradas. Por lo tanto, el código Hash de un objeto se almacena en una lista.
  • Ya arregladoTached (ver comentarios)
  • FormatValue corregido (ver comentarios)

¿Tal vez a través de JavaScriptSerializer.Serialize ?

En cuanto a TypeDescriptor de la respuesta de Sean (no puedo comentar porque tengo mala reputación) … una ventaja de usar TypeDescriptor sobre GetProperties () es que TypeDescriptor tiene un mecanismo para unir dinámicamente propiedades a objetos en tiempo de ejecución y la reflexión normal se perderá estos .

Por ejemplo, al trabajar con el objeto PSObject de PowerShell, que puede tener propiedades y métodos agregados en el tiempo de ejecución, implementaron un TypeDescriptor personalizado que fusiona estos miembros con el conjunto de miembros estándar. Al usar TypeDescriptor, su código no necesita conocer ese hecho.

Componentes, controles, y creo que tal vez los DataSets también hacen uso de esta API.

El siguiente fragmento hará la función deseada:

 Type t = obj.GetType();//where obj is object whose properties you need. PropertyInfo [] pi =t.GetProperties(); foreach (PropertyInfo p in pi) { System.Console.WriteLine(p.Name + " " + p.GetType); } 

Creo que si escribes esto como método de extensión podrías usarlo en todo tipo de objetos.

Esto es exactamente para lo que es la reflexión. No creo que haya una solución más simple, pero la reflexión no es tan intensiva en el código de todos modos.

Cualquier otra solución / biblioteca al final va a usar la reflexión para introspectar el tipo …

No lo creo Siempre tuve que escribirlos o usar el trabajo de otra persona para obtener esa información. Tiene que ser reflection por lo que yo sé.

EDITAR:
Mira esto . Estaba investigando algunas depuraciones en gráficos de objetos largos y noté esto cuando agregué Relojes, VS arroja en esta clase: Mscorlib_CollectionDebugView<> . Es un tipo interno para mostrar colecciones muy bien para ver en los modos de depuración de Windows / código de reloj. Ahora, como es interno, puede hacer referencia a él, pero puede usar Reflector para copiar (desde mscorlib) el código y tener el suyo (el enlace de arriba tiene un ejemplo de copiar / pegar). Parece realmente útil.