オブジェクト全体をC#のログにダンプする最良の方法は何ですか?


129

したがって、実行時に現在のオブジェクトの状態を表示するには、Visual Studioのイミディエイトウィンドウで表示されるものが本当に気に入っています。単純に

? objectname

オブジェクトの適切にフォーマットされた「ダンプ」を取得します。

コードでこれを行う簡単な方法はありますか?ログに記録するときに同様のことができますか?


最後に、T.Dumpをかなり使用しました。これはかなり確かな解決策です-再帰に注意する必要があるだけです。
Dan Esparza

これは古い質問ですが、多くの検索ヒットの上位に出てきます。今後の読者のために:これと拡張機能を参照してください。VS2015で私にとってはうまくいきました。
ジェシーグッド

1
そのVSプラグインは維持されておらず、一部の機能がないため、2020年の更新。次のライブラリはコードで同じことを行います-いくつかの追加機能があります。たとえば、ループを回避するためにすでにアクセスした場所を追跡します:github.com/thomasgalliker/ObjectDumper
Nick Westgate

回答:


55

Linqサンプルに付属しているObjectDumperコードをベースにすることもできます。サンプルを入手するに
は、この関連質問の回答もご覧ください。


5
また、配列に対しても機能しません(配列のタイプと長さを表示するだけで、その内容は出力しません)。
Konrad Morawski、2012

5
ObjectDumperのnugetパッケージが利用可能になりました。また、拡張メソッド提供DumpToStringDumpObjectクラスを。ハンディ。
IsmailS 2015年

2
w3wp.exeObjectDumperように使用しようとするとクラッシュするRequest.DumpToString("aaa");
Paul

60

より大きなオブジェクトグラフの場合は、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ビジュアライザーを選択します。

Visual Studio 2013の[ローカル]ウィンドウのスクリーンショット

Visual Studio 2013を使用しています。


2
SerializeObj-> SerializeObject?
ワイズマン

素晴らしい、ありがとう。リモートサーバーにVisual Studioのデバッグツールをインストールできません。これは、asp.net mvcアプリで非常にうまく機能します。
Liam Kernighan

1
素敵なフォーマットを行うには、次のようにしますNewtonsoft.Json.JsonConvert.SerializeObject(sampleData, Formatting.Indented)
。– Zorgarath

手作業で行うよりもはるかに簡単です。複雑になる
ahong

26

これを行うにはもっと良い方法があると確信していますが、以前は次のようなメソッドを使用して、オブジェクトをログに記録できる文字列にシリアル化しています。

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

メソッドもシリアル化されたオブジェクトではなく例外を返す可能性があることがわかるので、ログに記録するオブジェクトがシリアル化可能であることを確認する必要があります。


2
質問に回答した後にC#に追加された機能を考慮すると、この実装が拡張メソッドとして適切に機能することを指摘するのに役立ちます。Objectクラスに適用し、必要なすべての場所で拡張機能を参照する場合、関数を呼び出すのに便利です。
Nikita G.

私はこれから続けます:Failed to access type 'System.__ComObject' failed。Noobからc#へ、助けていただければ幸いです。
GuySoft 2014年

1
@GuySoftオブジェクトのプロパティの1つ、またはオブジェクト自体がシリアル化可能ではないようです。
Bernhard Hofmann

残念ながら、パラメーターなしのコンストラクターがないクラスでは、このメソッドを使用できません。それらはシリアライズ可能ではありません。
Jarekczek

22

Visual Studioイミディエイトウィンドウを使用できます

これを貼り付けるだけです(actual明らかにオブジェクト名を変更してください)。

Newtonsoft.Json.JsonConvert.SerializeObject(actual);

オブジェクトをJSONで出力する必要があります ここに画像の説明を入力してください

textmechanicテキストツールまたは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
    }
}

これにより、オブジェクトをダンプできます。

これにより時間を節約できることを願っています。


ありがとう。おそらくあなたは私の元の質問でそれを理解しなかったかもしれませんが、私はすでに即時ウィンドウについて知っていて、アプリにログインするときに同じことをしたかったことを示しました。
Dan Esparza 2015年

@DanEsparza Console.Log(Newtonsoft.Json.JsonConvert.SerializeObject(actual));?:)そしてはい、私は確かにそれを逃しました。この質問は、google.co.uk
Matas Vaitkevicius

2
参考までに、C#文字列にJSON文字列がある場合は、文字列の右側にあるスパイグラスアイコンをクリックして、Text Visualizerを選択します。JSON文字列のプレーンテキストバージョンを表示するウィンドウが表示されます(エスケープされた引用符や\ r \ nではありません)。
ウォルター

16

ServiceStack.Textには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
    }
}

1
フィールドでは機能しません。OPは「オブジェクト全体」について明示的に尋ねていました。
Konrad Morawski、2012

5
He didn't say fields-彼は言ったentire objects、それは分野を含みます。彼はまた、達成したいことの例としてVisual Studioのイミディエイトウィンドウ機能に言及しました(「単純な操作? objectnameを行うだけで、オブジェクトの適切にフォーマットされた 'ダンプ'が得られます」)。? objectnameすべてのフィールドも出力します。This has been immensely helpful - one of my most used extension methods to date-私はそれが有用であることに疑問を感じていません。それはオブジェクト全体をダンプすることだけです。
Konrad Morawski、2012

3
@KonradMorawski オブジェクト全体が正しくないということは、オブジェクトの再帰的ダンプを意味します。フィールドが含まれているのではなく、無限再帰ループに簡単につながる可能性があります。他の人が示唆していることを想定するべきではありません。私の回答は関連性と有用性の両方を備えていますが、あなたの反対票+コメントはそうではありません。
mythz 2012

1
@mythzはい、もちろんスタックオーバーフローを防ぐ必要があります(たとえば、すべてのInt32フィールドにフィールド自体MaxValueがありInt32ます...)。 -プロパティだけでなく、フィールドも含まれます。さらに、(表示さ? objectnameれなかった)表示フィールドにImmediate Window 、無限ループをトリガーせずに表示されます。それが私の反対票の場合は、取り下げることができます(ロックを解除して許可した場合)。とにかく私は原則的に同意しません。
Konrad Morawski、2012

4
-1は基本的にリンクのみの回答ですが、使用できれば素晴らしいようです。おそらく私は盲目ですが、そのリンクからソースを見つけることができません。2つのアップロードフォルダは空です。コードが長すぎて回答に含められませんか?

14

以下は、きれいにフォーマットされた、平らなオブジェクトを書く愚かな単純な方法です:

using Newtonsoft.Json.Linq;

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

何が起こっているのかというと、オブジェクトは最初にによってJSON内部表現にJObject.FromObject変換され、次にによってJSON文字列に変換されますToString。(そしてもちろん、JSON文字列は、特にToString改行とインデントが含まれるため、単純なオブジェクトの非常に優れた表現です。) "ToString"はもちろん無関係です(+文字列とオブジェクトを連結するためにを使用することで暗示されているため)。ここで指定したいです。


5
ログで快適に読み取るためのJsonConvert.SerializeObject(apprec、Formatting.Indented)
Tertium

1
HotLicks-この貢献が今私にとってどれほど重要であるかをお伝えしたいと思います。更新中に何が変更されたかの監査を提供する必要がありますが、あなたは私のストレスを「パニック」レベルから管理可能な「心配」レベルに戻したところです。ありがとうございます、あなたは非常に長く祝福された人生を送っていますか
Iofacture

4

リフレクションを使用してすべてのオブジェクトプロパティをループし、それらの値を取得してログに保存できます。書式設定は非常に簡単です(\ tを使用してオブジェクトのプロパティとその値をインデントできます)。

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

4

私が好きなのは、ToString()をオーバーライドして、型名以外の有用な出力を取得することです。これはデバッガーで便利です。オブジェクトを展開しなくても、オブジェクトに関する必要な情報を確認できます。


3

ObjectPrinterと呼ばれるライブラリを見つけました。このライブラリを使用すると、オブジェクトやコレクションを簡単に文字列(およびその他)にダンプできます。それはまさに私が必要としたことをします。


3

以下は、同じことを行う(およびネストされたプロパティを処理する)別のバージョンです。これは、より単純だと思います(外部ライブラリへの依存関係がなく、簡単に変更してロギング以外のことを実行できます)。

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
Date内部オブジェクトにプロパティがある場合、これはひどく死にます...ただ言う...
ノクティス

2

独自の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サンプルのオブジェクトダンパーを使用すると、時間を節約できます。


不思議なことに:これは配列やリストをどのように処理しますか?または親オブジェクトを参照するプロパティ?
Dan Esparza 2016年

@DanEsparzaより具体的にする方法を教えてくれてありがとう。
アリフルイスラム教

2

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

0

上記のすべてのパスは、オブジェクトがXMLまたはJSONにシリアル化可能であるか
、独自のソリューションを実装する必要があることを前提としています。

しかし、最終的には、次のような問題を解決する必要がある点に到達します

  • オブジェクトの再帰
  • シリアル化できないオブジェクト
  • 例外
  • ...

さらに、詳細情報が必要なログ:

  • イベントが発生したとき
  • 呼び出しスタック
  • どの三つ子
  • Webセッションの内容
  • どのIPアドレス
  • url
  • ...

これらすべてを解決する最良のソリューションがあります。
このNugetパッケージを使用してください:Desharp
すべてのタイプのアプリケーション(Webアプリケーションとデスクトップアプリケーションの両方)。Desharp Githubのドキュメントを
参照してください。それは持っている多くの設定オプションを

どこにでも電話する:

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
  • ログを素敵なHTML(またはTEXT形式、構成可能)で保存できます
  • オプションでバックグラウンドスレッドで書き込むことが可能(構成可能)
  • 最大オブジェクト深度と最大文字列長のオプションがあります(構成可能)
  • 反復可能なオブジェクトにはループを使用し、それ以外のすべてには後方反射を使用します。
    実際には、.NET環境で見つけることができるものすべてに使用します。

私はそれが役立つと信じています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.