web-dev-qa-db-fra.com

Quel est le meilleur moyen de vider des objets entiers dans un journal en C #?

Par conséquent, pour afficher l'état d'un objet actuel au moment de l'exécution, j'aime beaucoup ce que la fenêtre Visual Studio Immediate me fournit. Je fais juste un simple

? objectname

Me donnera un "dump" joliment formaté de l'objet.

Y a-t-il un moyen facile de faire cela dans le code, afin que je puisse faire quelque chose de similaire lors de la journalisation?

115
Dan Esparza

Vous pouvez baser quelque chose sur le code ObjectDumper fourni avec exemples Linq .
Regardez aussi la réponse de ceci question connexe pour obtenir un échantillon.

55
Mike Scott

Pour un graphe d'objet plus grand, j'appuie l'utilisation de Json mais avec une stratégie légèrement différente. J'ai tout d'abord une classe statique facile à appeler et une méthode statique qui enveloppe la conversion de Json (note: cela pourrait en faire une méthode d'extension).

using Newtonsoft.Json;

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

Puis dans votre Immediate Window,

var lookHere = F.Dump(myobj);

lookHere s'affichera automatiquement dans la fenêtre Locals précédée du symbole $ ou vous pourrez y ajouter une montre. Sur le côté droit de la colonne Value de l'inspecteur, vous trouverez une loupe avec un curseur déroulant à côté. Choisissez le curseur et choisissez le visualiseur Json.

Screenshot of Visual Studio 2013 Locals window

J'utilise Visual Studio 2013.

47
Jason

Je suis certain qu'il existe de meilleures façons de le faire, mais j'ai déjà utilisé une méthode semblable à celle-ci pour sérialiser un objet dans une chaîne que je peux consigner:

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

Vous verrez que la méthode peut également renvoyer l'exception plutôt que l'objet sérialisé. Vous devez donc vous assurer que les objets que vous souhaitez journaliser sont sérialisables.

25
Bernhard Hofmann

Vous pouvez utiliser Visual Studio Immediate Window

Collez simplement ceci (changez actual sur votre nom d'objet évidemment):

Newtonsoft.Json.JsonConvert.SerializeObject(actual);

Il devrait imprimer l'objet en JSON enter image description here

Vous devriez pouvoir le copier sur un outil texte textmechanic ou notepad ++ et remplacer les guillemets d'échappement (\") avec " et nouvelles lignes (\r\n) avec un espace vide, puis supprimez les guillemets doubles (") depuis le début et la fin et collez-le dans jsbeautifier pour le rendre plus lisible.

UPDATE au commentaire de l'OP

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

cela devrait vous permettre de vider n'importe quel objet.

J'espère que cela vous fait gagner du temps.

19
Matas Vaitkevicius

J'ai un méthode d'extension T.Dump () qui fait exactement cela, vide récursivement toutes les propriétés de tout type dans un format lisible par Nice.

Exemple d'utilisation:

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

et sortie:

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

Voici un moyen bêtement simple d'écrire un objet plat, bien formaté:

using Newtonsoft.Json.Linq;

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

Ce qui se passe, c'est que l'objet est d'abord converti en une représentation interne JSON par JObject.FromObject, puis converti en chaîne JSON par ToString. (Et bien sûr, une chaîne JSON est une très belle représentation d’un objet simple, d’autant plus que ToString inclura les sauts de ligne et les retraits.) Le "ToString" est évidemment superficiel (comme cela est implicite en utilisant + pour concaténer une chaîne et un objet), mais j'aime bien le spécifier ici.

14
Hot Licks

Vous pouvez utiliser la réflexion et parcourir toutes les propriétés de l'objet, puis obtenir leurs valeurs et les enregistrer dans le journal. La mise en forme est vraiment triviale (vous pouvez utiliser\t pour indenter les propriétés d’un objet et ses valeurs):

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

Ce que j'aime faire, c'est écraser ToString () pour obtenir une sortie plus utile au-delà du nom du type. C’est pratique dans le débogueur, vous pouvez voir les informations que vous souhaitez sur un objet sans avoir besoin de les développer.

3
Darryl Braaten

J'ai trouvé une bibliothèque appelée ObjectPrinter qui permet de vider facilement des objets et des collections en chaînes (et plus). Il fait exactement ce dont j'avais besoin.

3
Marek Dzikiewicz

Voici une autre version qui fait la même chose (et gère les propriétés imbriquées), ce qui, à mon avis, est plus simple (pas de dépendances sur des bibliothèques externes et peut être modifiée facilement pour faire autre chose que la journalisation):

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

Sur la base de @engineforce answer, j'ai créé cette classe que j'utilise dans un projet PCL d'une solution 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;
    }
}
2
gianlucaparadise

Vous pouvez écrire votre propre méthode 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());
    }

Utilisez-le comme-

WriteLine(myObject);

Pour écrire une collection, nous pouvons utiliser

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

La méthode peut ressembler

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

En utilisant if, else if et en vérifiant les interfaces, les attributs, le type de base, etc., et la récursivité (méthode récursive), nous pouvons ainsi obtenir un dumper d’objets, mais c’est fastidieux. L'utilisation du dumper d'objet de Microsoft LINQ Sample vous ferait gagner du temps.

2
Ariful Islam