そのため、実行時に現在のオブジェクトの状態を表示するために、Visual Studioイミディエイトウィンドウが提供するものが本当に気に入っています。簡単にやるだけ
? objectname
オブジェクトの適切にフォーマットされた「ダンプ」を提供します。
コードでこれを行う簡単な方法がありますので、ロギング時に同様のことができますか?
より大きなオブジェクトグラフの場合、Jsonを2番目に使用しますが、戦略は少し異なります。まず、呼び出しが簡単で、Json変換をラップする静的メソッドを持つ静的クラスがあります(注:これを拡張メソッドにすることができます)。
using Newtonsoft.Json;
public static class F
{
public static string Dump(object obj)
{
return JsonConvert.SerializeObject(obj);
}
}
次に、Immediate Window
、
var lookHere = F.Dump(myobj);
lookHereは、$が前に付いたLocals
ウィンドウに自動的に表示されます。または、ウォッチを追加できます。インスペクターのValue
列の右側には、横にドロップダウンキャレットが付いた虫眼鏡があります。ドロップダウンキャレットを選択し、Json Visualizerを選択します。
Visual Studio 2013を使用しています。
私はこれを行うためのより良い方法があると確信していますが、過去に次のようなメソッドを使用して、オブジェクトをログに記録できる文字列にシリアル化しました:
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;
}
メソッドは、シリアル化されたオブジェクトではなく例外を返すこともあるため、ログに記録するオブジェクトがシリアル化可能であることを確認する必要があります。
Visual Studioイミディエイトウィンドウを使用できます
これを貼り付けてください(明らかにactual
をオブジェクト名に変更してください):
Newtonsoft.Json.JsonConvert.SerializeObject(actual);
オブジェクトをJSONで印刷する必要があります
あなたはそれをコピーすることができるはずです over textmechanic text tool or notepad ++ そしてエスケープされた引用符を置き換える(\"
)"
および改行(\r\n
)スペースを空けてから、二重引用符("
)最初と最後から jsbeautifier に貼り付けて読みやすくします。
OPのコメントの更新
public static class Dumper
{
public static void Dump(this object obj)
{
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(obj)); // your logger
}
}
これにより、任意のオブジェクトをダンプできます。
これで時間を節約できることを願っています。
私は T.Dump()拡張メソッド を持っています。これはまさにこれを行い、ニースの読み取り可能な形式であらゆるタイプのすべてのプロパティを再帰的にダンプします。
使用例:
var model = new TestModel();
Console.WriteLine(model.Dump());
および出力:
{
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
}
}
きれいにフォーマットされたフラットなオブジェクトを書く愚かなシンプルな方法は次のとおりです。
using Newtonsoft.Json.Linq;
Debug.WriteLine("The object is " + JObject.FromObject(theObjectToDump).ToString());
起こっているのは、オブジェクトが最初にJObject.FromObject
によってJSON内部表現に変換され、次にToString
によってJSON文字列に変換されることです。 (もちろん、JSON文字列は、特にToString
が改行とインデントを含むため、単純なオブジェクトの非常に素晴らしい表現です。)「ToString」はもちろん無関係です(+
を使用して文字列とオブジェクト)、しかし、私はちょっとここでそれを指定したいです。
リフレクションを使用してすべてのオブジェクトプロパティをループし、それらの値を取得してログに保存できます。書式設定は本当に簡単です(\ tを使用してオブジェクトのプロパティとその値をインデントできます):
MyObject
Property1 = value
Property2 = value2
OtherObject
OtherProperty = value ...
ToString()をオーバーライドするのが好きなので、型名を超えたより有用な出力が得られます。これはデバッガで便利です。オブジェクトを展開することなく、オブジェクトについて必要な情報を表示できます。
ObjectPrinter というライブラリを見つけました。これにより、オブジェクトやコレクションを文字列(その他)に簡単にダンプできます。それはまさに私が必要としていたことをします。
以下は同じことを行う(およびネストされたプロパティを処理する)別のバージョンです。これは簡単だと思います(外部ライブラリに依存せず、ロギング以外のことを行うために簡単に変更できます)。
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"));
}
}
@engineforceの回答に基づいて、XamarinソリューションのPCLプロジェクトで使用しているこのクラスを作成しました。
/// <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;
}
}
独自の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());
}
次のように使用します
WriteLine(myObject);
コレクションを作成するには、次のようにします。
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);
}
}
メソッドは次のようになります。
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());
}
}
if, else if
を使用して、インターフェイス、属性、ベースタイプなどと再帰(これは再帰的なメソッドであるため)をチェックすることで、オブジェクトダンプを実現できますが、確かに退屈です。 MicrosoftのLINQ Sampleのオブジェクトダンパーを使用すると、時間を節約できます。