Qual è il modo migliore per scaricare interi oggetti in un registro in C #?


129

Quindi, per visualizzare lo stato di un oggetto corrente in fase di runtime, mi piace molto quello che mi dà la finestra di Visual Studio Immediate. Sto solo facendo un semplice

? objectname

Mi darà un 'dump' ben formattato dell'oggetto.

C'è un modo semplice per farlo nel codice, quindi posso fare qualcosa di simile durante la registrazione?


Alla fine, ho usato T.Dump un bel po '. È una soluzione abbastanza solida: devi solo fare attenzione alla ricorsione.
Dan Esparza,

Questa è una vecchia domanda, ma viene fuori in cima a molti risultati di ricerca. Per i lettori futuri: Vedere questo vs estensione . Ha funzionato alla grande per me in VS2015.
Jesse Good,

1
Aggiornamento per il 2020 poiché il plug-in VS non è gestito e manca di alcune funzionalità. La seguente libreria fa la stessa cosa nel codice - e ha alcune funzionalità extra, ad esempio tiene traccia di dove è già stata visitata per evitare loop: github.com/thomasgalliker/ObjectDumper
Nick Westgate

Risposte:


55

È possibile basare qualcosa sul codice ObjectDumper fornito con gli esempi Linq .
Dai anche un'occhiata alla risposta di questa domanda correlata per ottenere un campione.


5
Inoltre, non funziona per gli array (visualizza solo il tipo e la lunghezza dell'array, ma non stampa il suo contenuto).
Konrad Morawski,

5
Il pacchetto nuget per ObjectDumper è ora disponibile. Fornisce inoltre un metodo di estensione DumpToStringe Dumpdi Objectclasse. Maneggevole.
IsmailS,

2
w3wp.exesi arresta in modo anomalo quando provo a utilizzare ObjectDumperlikeRequest.DumpToString("aaa");
Paul

60

Per un grafico a oggetti più grande, secondo l'uso di Json ma con una strategia leggermente diversa. Innanzitutto ho una classe statica che è facile da chiamare e con un metodo statico che avvolge la conversione Json (nota: potrebbe rendere questo un metodo di estensione).

using Newtonsoft.Json;

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

Quindi nel tuo Immediate Window,

var lookHere = F.Dump(myobj);

lookHere verrà mostrato automaticamente nella Localsfinestra preceduto da $ o puoi aggiungere un orologio ad esso. Sul lato destro della Valuecolonna nell'ispettore, c'è una lente d'ingrandimento con un cursore a discesa accanto. Scegli il cursore a discesa e scegli Visualizzatore Json.

Schermata della finestra Locals di Visual Studio 2013

Sto usando Visual Studio 2013.


2
SerializeObj -> SerializeObject?
Wiseman,

Fantastico, grazie. Non riesco a installare strumenti di debug per Visual Studio sul mio server remoto e questa cosa funziona molto bene nella mia app asp.net mvc.
Liam Kernighan,

1
Per una buona formattazione puoi fare:Newtonsoft.Json.JsonConvert.SerializeObject(sampleData, Formatting.Indented)
Zorgarath

molto più facile che provare a farlo a mano. Diventa complicato
tra il

26

Sono certo che ci sono modi migliori per farlo, ma in passato ho usato un metodo simile al seguente per serializzare un oggetto in una stringa che posso registrare:

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

Vedrai che il metodo potrebbe anche restituire l'eccezione anziché l'oggetto serializzato, quindi assicurati che gli oggetti che desideri registrare siano serializzabili.


2
Alla luce delle funzionalità aggiunte a C # dopo aver risposto, la domanda potrebbe essere utile per sottolineare che questa implementazione funziona bene come metodo di estensione. Se applicato alla classe Object e si fa riferimento all'estensione ovunque sia necessaria, potrebbe essere un modo conveniente per chiamare la funzione.
Nikita G.

Continuo a ricevere da questo: Failed to access type 'System.__ComObject' failed. Noob to c #, apprezzerebbe l'aiuto.
GuySoft,

1
@GuySoft Sospetto che una delle proprietà sul tuo oggetto o l'oggetto stesso non sia serializzabile.
Bernhard Hofmann,

Purtroppo non è possibile utilizzare questo metodo su classi senza un costruttore senza parametri. Non sono serializzabili.
Jarekczek,

22

È possibile utilizzare la finestra immediata di Visual Studio

Basta incollarlo ( actualovviamente cambia il nome del tuo oggetto):

Newtonsoft.Json.JsonConvert.SerializeObject(actual);

Dovrebbe stampare l'oggetto in JSON inserisci qui la descrizione dell'immagine

Dovresti essere in grado di copiarlo sullo strumento di testo textmechanic o sul blocco note ++ e sostituire le virgolette ( \") con "e newline ( \r\n) con spazio vuoto, quindi rimuovere le virgolette doppie ( ") dall'inizio e dalla fine e incollarlo in jsbeautifier per renderlo più leggibile.

AGGIORNAMENTO al commento di OP

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

questo dovrebbe consentire di scaricare qualsiasi oggetto.

Spero che questo ti faccia risparmiare un po 'di tempo.


Grazie. Forse non l'hai preso nella mia domanda originale, ma ho indicato che sapevo già della finestra immediata e che volevo fare la stessa cosa quando accedevo alla mia app.
Dan Esparza,

@DanEsparza Console.Log(Newtonsoft.Json.JsonConvert.SerializeObject(actual));? :) e sì, mi è davvero mancato. Questa domanda sorge quando cerchi google.co.uk/…
Matas Vaitkevicius il

2
Cordiali saluti, quando si dispone di una stringa JSON in una stringa C #, fare clic sull'icona del cannocchiale a destra della stringa e selezionare Visualizzatore testo. Verrà visualizzata una finestra che mostra una versione di testo semplice della stringa JSON (virgolette non di escape o \ r \ n).
Walter,

17

ServiceStack.Text ha un metodo di estensione T.Dump () che fa esattamente questo, scarica in modo ricorsivo tutte le proprietà di qualsiasi tipo in un formato leggibile.

Esempio di utilizzo:

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

e uscita:

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

1
Non funziona per i campi. L'OP chiedeva esplicitamente "interi oggetti".
Konrad Morawski,

5
He didn't say fields- ha detto entire objects, che include i campi. Ha anche menzionato la funzionalità Finestra immediata di Visual Studio come esempio di ciò che voleva ottenere ( "Basta fare un semplice ? objectnamemi darà un 'dump' ben formattato dell'oggetto" ). ? objectnamestampa anche tutti i campi. This has been immensely helpful - one of my most used extension methods to date- Non metto in dubbio che sia utile, ma solo che scarica interi oggetti.
Konrad Morawski,

3
@KonradMorawski Interi oggetti errati significano un dump ricorsivo dell'oggetto, NON che include campi, che possono facilmente portare a un ciclo ricorsivo infinito. Non dovresti assumere ciò che gli altri stanno insinuando. La mia risposta è sia pertinente che utile, il tuo voto negativo + il commento non lo è.
Mythz,

1
@mythz sì, certo, è necessario evitare un overflow dello stack (es. ogni Int32campo ha un MaxValuecampo, che è un Int32se stesso ...), questo è un buon punto, ma non cambia il fatto che gli oggetti - e certamente quelli interi - consistono anche in campi, non solo proprietà. Cosa c'è di più (che non ha affrontato quella), ? objectnamenei Immediate Window does campi di visualizzazione - senza innescare un ciclo infinito. Se riguarda il mio downvote, posso ritirarlo (se mi permetti di sbloccarlo, cioè). In linea di principio non sono d'accordo.
Konrad Morawski,

4
-1 essenzialmente per una risposta solo link, anche se sembra bello se potessi usarlo! Forse sono cieco, ma non riesco a trovare la fonte tramite quel link; le due cartelle di caricamento sono vuote. Il codice è troppo lungo per essere incluso nella risposta?

14

Ecco un modo stupidamente semplice per scrivere un oggetto piatto, ben formattato:

using Newtonsoft.Json.Linq;

Debug.WriteLine("The object is " + JObject.FromObject(theObjectToDump).ToString());

Quello che sta succedendo è che l'oggetto viene prima convertito in una rappresentazione interna JSON da JObject.FromObject, quindi convertito in stringa JSON da ToString. (E ovviamente una stringa JSON è una rappresentazione molto bella di un oggetto semplice, soprattutto perché ToStringincluderà righe e rientri.) Il "ToString" è ovviamente estraneo (come è implicito usando +per concatenare una stringa e un oggetto), ma Mi piacerebbe specificarlo qui.


5
JsonConvert.SerializeObject (apprezzo, formattazione, rientro) per una lettura confortevole nel registro
Tertium

1
HotLicks - Voglio dirti quanto sia importante questo contributo per me in questo momento. Ho l'obbligo di fornire un controllo di ciò che è cambiato durante un aggiornamento e hai appena riportato il mio stress dal livello di "panico" a un livello di "preoccupazione" gestibile. Grazie signore, possa avere una vita molto lunga e benedetta
Iofacture

4

È possibile utilizzare la riflessione e scorrere tutte le proprietà dell'oggetto, quindi ottenere i loro valori e salvarli nel registro. La formattazione è davvero banale (puoi usare \ t per indentare le proprietà di un oggetto e i suoi valori):

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

4

Quello che mi piace fare è sovrascrivere ToString () in modo da ottenere un output più utile oltre il nome del tipo. Questo è utile nel debugger, puoi vedere le informazioni che desideri su un oggetto senza bisogno di espanderlo.


3

Ho trovato una libreria chiamata ObjectPrinter che consente di scaricare facilmente oggetti e raccolte nelle stringhe (e altro). Fa esattamente quello di cui avevo bisogno.


3

Di seguito è un'altra versione che fa la stessa cosa (e gestisce le proprietà nidificate), che penso sia più semplice (nessuna dipendenza da librerie esterne e può essere modificata facilmente per fare cose diverse dalla registrazione):

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

1
questo morirà orribilmente se hai una Dateproprietà nel tuo oggetto interiore ... sto solo dicendo ...
Noctis

2

Puoi scrivere il tuo metodo 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());
    }

Usalo come-

WriteLine(myObject);

Per scrivere una raccolta possiamo usare-

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

Il metodo può apparire come-

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

Utilizzando if, else ife controllando interfacce, attributi, tipo di base, ecc. E ricorsione (in quanto questo è un metodo ricorsivo) in questo modo possiamo ottenere un oggetto dumper, ma è sicuramente noioso. L'uso del dumper oggetto dall'esempio LINQ di Microsoft consente di risparmiare tempo.


Per curiosità: come gestisce le matrici o gli elenchi? O proprietà che fanno riferimento a oggetti padre?
Dan Esparza

@DanEsparza Grazie per avermi mostrato il modo di essere più specifico.
Ariful Islam

2

Sulla base della risposta di @engineforce, ho creato questa classe che sto usando in un progetto PCL di una soluzione 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;
    }
}

0

Tutti i percorsi sopra riportati presuppongono che i tuoi oggetti siano serializzabili in XML o JSON,
oppure devi implementare la tua soluzione.

Ma alla fine arrivi ancora al punto in cui devi risolvere problemi come

  • ricorsione negli oggetti
  • oggetti non serializzabili
  • eccezioni
  • ...

Inoltre registro vuoi maggiori informazioni:

  • quando si è verificato l'evento
  • callstack
  • quale tre
  • cosa c'era nella sessione web
  • quale indirizzo IP
  • url
  • ...

C'è la migliore soluzione che risolve tutto questo e molto altro.
Usa questo pacchetto Nuget: Desharp .
Per tutti i tipi di applicazioni, sia Web che desktop .
Guarda la documentazione di Desharp Github . Ha molte opzioni di configurazione .

Chiama ovunque:

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
  • può salvare il registro in un bel HTML (o in formato TEXT, configurabile)
  • è possibile scrivere facoltativamente nel thread in background (configurabile)
  • ha opzioni per la profondità massima degli oggetti e la lunghezza massima delle stringhe (configurabile)
  • utilizza loop per oggetti iterabili e riflessioni all'indietro per tutto il resto,
    anzi per tutto ciò che puoi trovare nell'ambiente .NET .

Credo che aiuterà.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.