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

129

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

? objectname

Me dará un 'volcado' bien formateado del objeto.

¿Hay una manera fácil de hacer esto en código, para que pueda hacer algo similar al iniciar sesión?

Dan Esparza
fuente
Al final, he usado T.Dump bastante. Es una solución bastante sólida: solo debe tener cuidado con la recursividad.
Dan Esparza
Esta es una vieja pregunta, pero aparece en la parte superior de muchos resultados de búsqueda. Para los lectores futuros: Ver este vs extensión . Me funcionó muy bien en VS2015.
Jesse Good
1
Actualice para 2020 ya que ese complemento VS no se mantiene y carece de algunas características. La siguiente biblioteca hace lo mismo en el código, y tiene algunas características adicionales, por ejemplo, rastrea dónde ya se ha visitado para evitar bucles: github.com/thomasgalliker/ObjectDumper
Nick Westgate

Respuestas:

55

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

Mike Scott
fuente
55
Tampoco funciona para matrices (solo muestra el tipo y la longitud de la matriz, pero no imprime su contenido).
Konrad Morawski
55
El paquete nuget para ObjectDumper ya está disponible. También proporciona un método de extensión DumpToStringy Dumpde Objectclase. Práctico.
IsmailS
2
w3wp.exese bloquea cuando intento usar ObjectDumperlikeRequest.DumpToString("aaa");
Paul
60

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 de Json (nota: podría hacer de este un método de extensión).

using Newtonsoft.Json;

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

Entonces en tu Immediate Window,

var lookHere = F.Dump(myobj);

lookHere se mostrará automáticamente en la Localsventana antepuesta con un $ o puede agregarle un reloj. En el lado derecho de la Valuecolumna 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 Visual Studio 2013 Locals

Estoy usando Visual Studio 2013.

Jason
fuente
2
SerializeObj -> SerializeObject?
Wiseman
Genial, gracias. No puedo instalar herramientas de depuración para Visual Studio en mi servidor remoto, y esto funciona extremadamente bien en mi aplicación asp.net mvc.
Liam Kernighan el
1
Para un buen formato puede hacer:Newtonsoft.Json.JsonConvert.SerializeObject(sampleData, Formatting.Indented)
Zorgarath
mucho más fácil que intentar hacerlo a mano. Se complica
ahong
26

Estoy seguro de que hay mejores formas de hacerlo, pero en el pasado he usado un método similar al 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.

Bernhard Hofmann
fuente
2
A la luz de las características agregadas a C # después de responder la pregunta, puede ser útil señalar que esta implementación funciona muy bien como método de extensión. Si se aplica a la clase Object y hace referencia a la extensión donde sea que la necesite, podría ser una forma conveniente de llamar a la función.
Nikita G.
Sigo obteniendo de esta: Failed to access type 'System.__ComObject' failed. Novato a C #, agradecería ayuda.
GuySoft
1
@GuySoft Sospecho que una de las propiedades de su objeto, o el objeto en sí, no es serializable.
Bernhard Hofmann
Desafortunadamente, no puede usar este método en clases sin un constructor sin parámetros. No son serializables.
Jarekczek
22

Puede usar la ventana Inmediato de Visual Studio

Simplemente pegue esto (cambie actualsu nombre de objeto obviamente):

Newtonsoft.Json.JsonConvert.SerializeObject(actual);

Debería imprimir el objeto en JSON ingrese la descripción de la imagen aquí

Debería poder copiarlo sobre la herramienta de texto textmechanic o notepad ++ y reemplazar las comillas escapadas ( \") con "y las líneas nuevas ( \r\n) con un espacio vacío, luego elimine las comillas dobles ( ") desde el principio y el final y péguelo en jsbeautifier para que sea 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.

Matas Vaitkevicius
fuente
Gracias. Tal vez no lo entendiste en mi pregunta original, pero indiqué que ya sabía sobre la ventana inmediata y que quería hacer lo mismo al iniciar sesión en mi aplicación.
Dan Esparza
@DanEsparza Console.Log(Newtonsoft.Json.JsonConvert.SerializeObject(actual));? :) y sí, de hecho lo extrañé. Esta pregunta surge cuando busca en google.co.uk/…
Matas Vaitkevicius
2
Para su información, cuando tiene una cadena JSON en una cadena C #, haga clic en el icono del catalejo a la derecha de la cadena y seleccione el Visualizador de texto. Aparecerá una ventana que muestra una versión de texto sin formato de la cadena JSON (sin comillas escapadas o \ r \ n).
Walter
17

ServiceStack.Text tiene un método de extensión T.Dump () que hace exactamente esto, volca recursivamente todas las propiedades de cualquier tipo en un formato agradable y 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
    }
}
mythz
fuente
1
No funciona para campos. El OP preguntaba explícitamente sobre "objetos completos".
Konrad Morawski
55
He didn't say fields- dijo él entire objects, que incluye campos. También mencionó la característica de Ventana Inmediata de Visual Studio como ejemplo de lo que quería lograr ( "Simplemente hacer un simple ? objectnameme dará un 'volcado' del objeto muy bien formateado" ). ? objectnameimprime todos los campos también. This has been immensely helpful - one of my most used extension methods to date- No cuestiono que sea útil, solo que arroja objetos enteros.
Konrad Morawski
3
@KonradMorawski Objetos enteros incorrectos significa un volcado recursivo del objeto, NO que incluya campos, que pueden conducir fácilmente a un bucle recursivo infinito. No debes asumir lo que otros están implicando. Mi respuesta es relevante y útil, tu voto negativo + comentario no lo es.
mythz
1
@mythz sí, por supuesto, debe evitar un desbordamiento de la pila (por ejemplo, cada Int32campo tiene un MaxValuecampo, que es un Int32sí mismo ...), ese es un buen punto, pero no cambia el hecho de que los objetos, y ciertamente los enteros - también consisten en campos, no solo propiedades. Lo que es más (que no se refirió a que uno), ? objectnameen las Immediate Window EOD campos de visualización - sin provocar un bucle infinito. Si se trata de mi voto negativo, puedo retirarlo (si me lo permite desbloqueándolo, claro). No estoy de acuerdo en principio de todos modos.
Konrad Morawski
44
-1 para esencialmente una respuesta de solo enlace, ¡aunque se ve genial si pudiera usarla! Quizás soy ciego, pero no puedo encontrar la fuente a través de ese enlace; Las dos carpetas de carga están vacías. ¿El código es demasiado largo para incluirlo en la respuesta?
14

Aquí hay una manera estúpidamente simple de escribir un objeto plano, 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 en una representación interna JSON por JObject.FromObject, y luego se convierte en una cadena JSON por ToString. (Y, por supuesto, una cadena JSON es una muy buena representación de un objeto simple, especialmente porque ToStringincluirá nuevas líneas y sangrías). El "ToString" es, por supuesto, extraño (como se implica al usar +para concatenar una cadena y un objeto), pero Me gusta especificarlo aquí.

Hot Licks
fuente
55
JsonConvert.SerializeObject (apprec, Formatting.Indented) para una lectura cómoda en el registro
Tertium
1
HotLicks: quiero transmitirles lo importante que es esta contribución para mí en este momento. Tengo el requisito de proporcionar una auditoría de lo que cambió durante una actualización y usted acaba de llevar mi estrés del nivel de "pánico" a un nivel manejable de "preocupación". Gracias señor, que tenga una vida muy larga y bendecida
Iofacture
4

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

MyObject
    Property1 = value
    Property2 = value2
    OtherObject
       OtherProperty = value ...
Ricardo Villamil
fuente
4

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

Darryl Braaten
fuente
3

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

Marek Dzikiewicz
fuente
3

La siguiente es otra versión que hace lo mismo (y maneja las propiedades anidadas), que creo que es más simple (no depende de bibliotecas externas y se puede modificar fácilmente para hacer otras cosas que no sean el 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() : "<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"));
    }
}
fuerza de motor
fuente
1
esto morirá horriblemente si tienes una Datepropiedad en tu objeto interior ... solo diciendo ...
Noctis
2

Puedes escribir tu propio método 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());
    }

Ú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 parecer-

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

Usando if, else ify comprobando interfaces, atributos, tipo base, etc. y recursividad (ya que este es un método recursivo) de esta manera podemos lograr un objeto volcado, pero es tedioso con seguridad. Usar el volcador de objetos de la muestra LINQ de Microsoft le ahorraría tiempo.

Islam Ariful
fuente
Por curiosidad: ¿cómo maneja esto las matrices o listas? ¿O propiedades que hacen referencia a objetos primarios?
Dan Esparza
@ DanEsparza Gracias por mostrarme la forma de ser más específico.
Ariful Islam
2

Basado en la respuesta de @engineforce, hice esta clase que estoy usando en un proyecto PCL de una Solución Xamarin:

/// <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;
    }
}
gianlucaparadise
fuente
0

Todas las rutas anteriores suponen que sus objetos son serializables a XML o JSON,
o debe implementar su propia solución.

Pero al final todavía llegas al punto en que tienes que resolver problemas como

  • recursividad en objetos
  • objetos no serializables
  • excepciones
  • ...

Además inicie sesión desea más información:

  • cuando sucedió el evento
  • pila de llamadas
  • cual threead
  • lo que había en la sesión web
  • cual dirección ip
  • url
  • ...

Existe la mejor solución que resuelve todo esto y mucho más.
Use este paquete Nuget : Desharp .
Para todo tipo de aplicaciones, tanto aplicaciones web como de escritorio .
Vea que es la documentación de Desharp Github . Tiene muchas opciones de configuración .

Simplemente llame a cualquier parte:

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
  • puede guardar el registro en HTML agradable (o en formato TEXTO, configurable)
  • es posible escribir opcionalmente en hilo de fondo (configurable)
  • Tiene opciones para la profundidad máxima de los objetos y la longitud máxima de las cadenas (configurable)
  • utiliza bucles para objetos iterables y reflexión hacia atrás para todo lo demás, de
    hecho para cualquier cosa que pueda encontrar en el entorno .NET .

Creo que te ayudará.

Tom Flídr
fuente