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?
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.
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.
J'utilise Visual Studio 2013.
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.
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
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.
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
}
}
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.
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 ...
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.
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.
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"));
}
}
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;
}
}
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.