Was ist der beste Weg, um ganze Objekte in einem Protokoll in C # abzulegen?

129

Um den Status eines aktuellen Objekts zur Laufzeit anzuzeigen, gefällt mir sehr, was mir das Visual Studio-Sofortfenster bietet. Einfach ein einfaches machen

? objectname

Gibt mir einen schön formatierten 'Dump' des Objekts.

Gibt es eine einfache Möglichkeit, dies im Code zu tun, damit ich beim Protokollieren etwas Ähnliches tun kann?

Dan Esparza
quelle
Am Ende habe ich T.Dump ziemlich oft benutzt. Es ist eine ziemlich solide Lösung - Sie müssen nur auf Rekursion achten.
Dan Esparza
Dies ist eine alte Frage, die jedoch bei vielen Suchtreffern ganz oben steht. Für zukünftige Leser: Siehe diese vs Erweiterung . Hat in VS2015 hervorragend für mich funktioniert.
Jesse Good
1
Update für 2020, da dieses VS-Plugin nicht gewartet wird und einige Funktionen fehlen. Die folgende Bibliothek macht dasselbe im Code - und sie hat einige zusätzliche Funktionen, z. B. verfolgt sie, wo sie bereits besucht wurde, um Schleifen zu vermeiden: github.com/thomasgalliker/ObjectDumper
Nick Westgate

Antworten:

55

Sie könnten etwas auf dem ObjectDumper-Code basieren, der mit den Linq-Beispielen geliefert wird .
Schauen Sie sich auch die Antwort auf diese verwandte Frage an, um ein Beispiel zu erhalten.

Mike Scott
quelle
5
Es funktioniert auch nicht für Arrays (es zeigt nur den Typ und die Länge des Arrays an, druckt aber nicht dessen Inhalt).
Konrad Morawski
5
Das Nuget-Paket für ObjectDumper ist jetzt verfügbar. Es bietet auch eine Erweiterungsmethode DumpToStringund Dumpfür die ObjectKlasse. Praktisch.
IsmailS
2
w3wp.exestürzt ab, wenn ich versuche, ObjectDumperlike zu verwendenRequest.DumpToString("aaa");
Paul
60

Für ein größeres Objektdiagramm unterstütze ich die Verwendung von Json, jedoch mit einer etwas anderen Strategie. Zuerst habe ich eine statische Klasse, die einfach aufzurufen ist und eine statische Methode enthält, die die Json-Konvertierung umschließt (Hinweis: Dies könnte eine Erweiterungsmethode sein).

using Newtonsoft.Json;

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

Dann in deinem Immediate Window,

var lookHere = F.Dump(myobj);

lookHere wird automatisch in dem LocalsFenster angezeigt, dem ein $ vorangestellt ist, oder Sie können eine Uhr hinzufügen. Auf der rechten Seite der ValueSäule im Inspektor befindet sich eine Lupe mit einem Dropdown-Caret daneben. Wählen Sie das Dropdown-Caret und dann Json Visualizer.

Screenshot des Visual Studio 2013 Locals-Fensters

Ich verwende Visual Studio 2013.

Jason
quelle
2
SerializeObj -> SerializeObject?
Wiseman
Genial, danke. Ich kann keine Debug-Tools für Visual Studio auf meinem Remote-Server installieren, und diese Funktion funktioniert sehr gut in meiner asp.net mvc-App.
Liam Kernighan
1
Für eine schöne Formatierung können Sie Newtonsoft.Json.JsonConvert.SerializeObject(sampleData, Formatting.Indented)
Folgendes
so viel einfacher als es von Hand zu versuchen. Es wird kompliziert
ahong
26

Ich bin mir sicher, dass es dafür bessere Möglichkeiten gibt, aber ich habe in der Vergangenheit eine Methode wie die folgende verwendet, um ein Objekt in eine Zeichenfolge zu serialisieren, die ich protokollieren kann:

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

Sie werden sehen, dass die Methode möglicherweise auch die Ausnahme anstelle des serialisierten Objekts zurückgibt. Sie sollten daher sicherstellen, dass die Objekte, die Sie protokollieren möchten, serialisierbar sind.

Bernhard Hofmann
quelle
2
In Anbetracht der Funktionen, die C # nach der Beantwortung hinzugefügt wurden, kann die Frage hilfreich sein, um darauf hinzuweisen, dass diese Implementierung als Erweiterungsmethode gut funktioniert. Wenn es auf die Objektklasse angewendet wird und Sie die Erweiterung überall dort referenzieren, wo Sie sie benötigen, kann dies eine bequeme Möglichkeit sein, die Funktion aufzurufen.
Nikita G.
Ich komme immer wieder davon ab : Failed to access type 'System.__ComObject' failed. Noob zu c #, würde mich über Hilfe freuen.
GuySoft
1
@GuySoft Ich vermute, dass eine der Eigenschaften Ihres Objekts oder das Objekt selbst nicht serialisierbar ist.
Bernhard Hofmann
Sie können diese Methode leider nicht für Klassen ohne einen parameterlosen Konstruktor verwenden. Sie sind nicht serialisierbar.
Jarekczek
22

Sie können Visual Studio Immediate Window verwenden

Fügen Sie dies einfach ein (ändern actualSie offensichtlich Ihren Objektnamen):

Newtonsoft.Json.JsonConvert.SerializeObject(actual);

Es sollte ein Objekt in JSON drucken Geben Sie hier die Bildbeschreibung ein

Sie sollten in der Lage sein, es über ein textmechanisches Textwerkzeug oder einen Editor ++ zu kopieren und maskierte Anführungszeichen ( \") durch "und Zeilenumbrüche ( \r\n) durch Leerzeichen zu ersetzen. Entfernen Sie dann doppelte Anführungszeichen ( ") von Anfang und Ende und fügen Sie es in ein jsbeautifier ein , um es besser lesbar zu machen.

UPDATE zum Kommentar von OP

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

Dies sollte es Ihnen ermöglichen, jedes Objekt zu sichern.

Hoffe das spart dir etwas Zeit.

Matas Vaitkevicius
quelle
Vielen Dank. Vielleicht haben Sie es in meiner ursprünglichen Frage nicht verstanden, aber ich habe angegeben, dass ich bereits über das unmittelbare Fenster Bescheid wusste, und ich wollte dasselbe tun, wenn ich mich in meiner App anmelde.
Dan Esparza
@ DanEsparza Console.Log(Newtonsoft.Json.JsonConvert.SerializeObject(actual));? :) und ja ich habe es wirklich verpasst. Diese Frage taucht auf, wenn Sie google.co.uk/…
Matas Vaitkevicius
2
Zu Ihrer Information: Wenn Sie eine JSON-Zeichenfolge in einer C # -String haben, klicken Sie auf das Fernglas-Symbol rechts neben der Zeichenfolge und wählen Sie den Text Visualizer aus. Es wird ein Fenster geöffnet, in dem eine Nur-Text-Version der JSON-Zeichenfolge angezeigt wird (keine maskierten Anführungszeichen oder \ r \ n).
Walter
16

ServiceStack.Text verfügt über eine T.Dump () - Erweiterungsmethode , die genau dies tut und rekursiv alle Eigenschaften eines beliebigen Typs in einem gut lesbaren Format .

Anwendungsbeispiel:

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

und Ausgabe:

{
    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
    }
}
Mythos
quelle
1
Es funktioniert nicht für Felder. Das OP fragte explizit nach "ganzen Objekten".
Konrad Morawski
5
He didn't say fields- sagte er entire objects, die Felder enthält. Er erwähnte auch die Funktion "Sofortiges Fenster" von Visual Studio als Beispiel dafür, was er erreichen wollte ( "Wenn ich nur ein einfaches mache ? objectname, bekomme ich einen schön formatierten 'Dump' des Objekts" ). ? objectnamedruckt auch alle Felder aus. This has been immensely helpful - one of my most used extension methods to date- Ich bezweifle nicht, dass es nützlich ist, sondern nur, dass ganze Objekte ausgegeben werden.
Konrad Morawski
3
@KonradMorawski Falsche ganze Objekte bedeuten einen rekursiven Speicherauszug des Objekts, NICHT, dass es Felder enthält, was leicht zu einer unendlichen rekursiven Schleife führen kann. Sie sollten nicht annehmen, was andere implizieren. Meine Antwort ist sowohl relevant als auch hilfreich, Ihre Ablehnung + Kommentar nicht.
Mythos
1
@mythz Ja, natürlich müssen Sie einen Stapelüberlauf verhindern (z. B. jedes Int32Feld hat ein MaxValueFeld, das ein Selbst ist Int32...), das ist ein guter Punkt, aber es ändert nichts an der Tatsache, dass Objekte - und sicherlich ganze - bestehen auch aus Feldern, nicht nur aus Eigenschaften. Was mehr ist (du hast es nicht Adresse , dass man), ? objectnamein dem Immediate Window tut Anzeigefeldern - ohne eine Endlosschleife auslösen. Wenn es um meine Ablehnung geht, kann ich sie zurückziehen (wenn Sie mich durch Entsperren lassen, das heißt). Ich bin sowieso grundsätzlich anderer Meinung.
Konrad Morawski
4
-1 für im Wesentlichen eine Nur-Link-Antwort, obwohl es großartig aussieht, wenn ich sie verwenden könnte! Vielleicht bin ich blind, aber ich kann über diesen Link keine Quelle finden. Die beiden Upload-Ordner sind leer. Ist der Code zu lang, um in die Antwort aufgenommen zu werden?
14

Hier ist eine dumm einfache Möglichkeit, ein flaches Objekt zu schreiben, das gut formatiert ist:

using Newtonsoft.Json.Linq;

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

Was passiert ist, dass das Objekt zuerst von in eine interne JSON-Darstellung JObject.FromObjectkonvertiert und dann von in eine JSON-Zeichenfolge konvertiert wird ToString. (Und natürlich ist eine JSON-Zeichenfolge eine sehr schöne Darstellung eines einfachen Objekts, zumal ToStringsie Zeilenumbrüche und Einrückungen enthält.) Der "ToString" ist natürlich irrelevant (wie es impliziert wird +, wenn eine Zeichenfolge und ein Objekt verwendet werden), aber Ich möchte es hier irgendwie spezifizieren.

Hot Licks
quelle
5
JsonConvert.SerializeObject (Wertschätzung, Formatierung.Indentiert) für komfortables Lesen im Protokoll
Tertium
1
HotLicks - Ich möchte Ihnen vermitteln, wie wichtig dieser Beitrag für mich im Moment ist. Ich muss ein Audit darüber durchführen, was sich während eines Updates geändert hat, und Sie haben gerade meinen Stress von der Panikstufe auf eine überschaubare Sorgenstufe reduziert. Vielen Dank, Sir, mögen Sie ein sehr langes und gesegnetes Leben haben
Iofacture
4

Sie können Reflection verwenden und alle Objekteigenschaften durchlaufen, dann ihre Werte abrufen und im Protokoll speichern. Die Formatierung ist wirklich trivial (Sie können \ t verwenden, um die Eigenschaften eines Objekts und seine Werte einzurücken):

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

Was ich gerne mache, ist das Überschreiben von ToString (), damit ich über den Typnamen hinaus eine nützlichere Ausgabe bekomme. Dies ist praktisch im Debugger. Sie können die gewünschten Informationen zu einem Objekt anzeigen, ohne es erweitern zu müssen.

Darryl Braaten
quelle
3

Das Folgende ist eine andere Version, die dasselbe tut (und verschachtelte Eigenschaften behandelt), was meiner Meinung nach einfacher ist (keine Abhängigkeiten von externen Bibliotheken und einfach zu ändern, um andere Dinge als die Protokollierung zu tun):

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"));
    }
}
Motorkraft
quelle
1
Dies wird schrecklich sterben, wenn Sie eine DateEigenschaft in Ihrem inneren Objekt haben ... nur sagen ...
Noctis
2

Sie können Ihre eigene WriteLine-Methode schreiben.

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

Verwenden Sie es wie-

WriteLine(myObject);

Um eine Sammlung zu schreiben, können wir verwenden:

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

Die Methode kann wie folgt aussehen:

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

Wenn if, else ifwir Schnittstellen, Attribute, Basistyp usw. und Rekursion (da dies eine rekursive Methode ist) verwenden und überprüfen, können wir auf diese Weise einen Objektdumper erzielen, der jedoch mit Sicherheit langwierig ist. Die Verwendung des Objektdummers aus dem LINQ-Beispiel von Microsoft würde Ihre Zeit sparen.

Schöner Islam
quelle
Aus Neugier: Wie geht das mit Arrays oder Listen um? Oder Eigenschaften, die auf übergeordnete Objekte verweisen?
Dan Esparza
@ DanEsparza Danke, dass du mir den Weg zeigst, genauer zu sein.
Ariful Islam
2

Basierend auf der Antwort von @engineforce habe ich diese Klasse erstellt, die ich in einem PCL-Projekt einer Xamarin-Lösung verwende:

/// <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
quelle
0

Bei allen oben genannten Pfaden wird davon ausgegangen, dass Ihre Objekte in XML oder JSON serialisierbar sind.
oder Sie müssen Ihre eigene Lösung implementieren.

Aber am Ende kommt man immer noch an den Punkt, an dem man Probleme wie lösen muss

  • Rekursion in Objekten
  • nicht serialisierbare Objekte
  • Ausnahmen
  • ...

Plus Protokoll Sie möchten weitere Informationen:

  • als das Ereignis passierte
  • Callstack
  • welche drei
  • Was war in der Web-Sitzung
  • welche IP-Adresse
  • URL
  • ...

Es gibt die beste Lösung, die all dies und noch viel mehr löst.
Verwenden Sie dieses Nuget-Paket: Desharp .
Für alle Arten von Anwendungen - sowohl Web- als auch Desktop-Anwendungen .
Siehe Desharp Github-Dokumentation . Es gibt viele Konfigurationsoptionen .

Rufen Sie einfach irgendwo an:

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
  • Es kann das Protokoll in schönem HTML (oder im konfigurierbaren TEXT-Format) speichern.
  • Es ist möglich, optional im Hintergrund-Thread zu schreiben (konfigurierbar).
  • Es verfügt über Optionen für die maximale Objekttiefe und die maximale Zeichenfolgenlänge (konfigurierbar).
  • Es verwendet Schleifen für iterierbare Objekte und Rückwärtsreflexion für alles andere, in der
    Tat für alles , was Sie in einer .NET-Umgebung finden können .

Ich glaube es wird helfen.

Tom Flídr
quelle