Apa cara terbaik untuk membuang seluruh objek ke log di C #?

129

Jadi untuk melihat status objek saat ini saat runtime, saya sangat suka apa yang diberikan jendela Visual Studio Immediate. Cukup melakukan yang sederhana

? objectname

Akan memberi saya 'dump' yang diformat dengan baik dari objek.

Apakah ada cara mudah untuk melakukan ini dalam kode, jadi saya bisa melakukan sesuatu yang serupa saat login?

Dan Esparza
sumber
Pada akhirnya, saya sudah menggunakan T.Dump sedikit. Ini solusi yang cukup solid - Anda hanya perlu berhati-hati terhadap rekursi.
Dan Esparza
Ini adalah pertanyaan lama, tetapi muncul di bagian atas dari banyak pencarian pencarian. Untuk pembaca masa depan: Lihat ini vs ekstensi . Bekerja bagus untuk saya di VS2015.
Jesse Good
1
Pembaruan untuk tahun 2020 karena plugin VS tidak dikelola dan tidak memiliki beberapa fitur. Pustaka berikut melakukan hal yang sama dalam kode - dan memiliki beberapa fitur tambahan, misalnya melacak di mana ia sudah dikunjungi untuk menghindari loop: github.com/thomasgalliker/ObjectDumper
Nick Westgate

Jawaban:

55

Anda bisa mendasarkan sesuatu pada kode ObjectDumper yang dikirimkan bersama sampel Linq .
Lihat juga jawaban pertanyaan terkait ini untuk mendapatkan sampel.

Mike Scott
sumber
5
Ini juga tidak berfungsi untuk array (hanya menampilkan jenis dan panjang array, tetapi tidak mencetak kontennya).
Konrad Morawski
5
paket nuget untuk ObjectDumper sekarang tersedia. Ini juga menyediakan metode ekstensi DumpToStringdan Dumpke Objectkelas. Berguna.
Ismail
2
w3wp.exemacet ketika saya mencoba menggunakan ObjectDumpersepertiRequest.DumpToString("aaa");
Paul
60

Untuk grafik objek yang lebih besar, saya menggunakan Json kedua tetapi dengan strategi yang sedikit berbeda. Pertama saya memiliki kelas statis yang mudah dipanggil dan dengan metode statis yang membungkus konversi Json (catatan: bisa menjadikan ini metode ekstensi).

using Newtonsoft.Json;

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

Kemudian di Immediate Window,

var lookHere = F.Dump(myobj);

lookHere akan muncul secara otomatis di Localsjendela yang diawali dengan $ atau Anda dapat menambahkan arloji ke sana. Di sisi kanan Valuekolom di inspektur, ada kaca pembesar dengan tanda dropdown di sampingnya. Pilih tanda panah dropdown dan pilih Json visualizer.

Cuplikan layar jendela Visual Studio 2013

Saya menggunakan Visual Studio 2013.

Jason
sumber
2
SerializeObj -> SerializeObject?
Wiseman
Cemerlang, terima kasih. Saya tidak dapat menginstal alat debug untuk Visual Studio di server jarak jauh saya, dan hal ini berfungsi sangat baik di aplikasi MVC asp.net saya.
Liam Kernighan
1
Untuk pemformatan yang bagus, Anda dapat melakukan:Newtonsoft.Json.JsonConvert.SerializeObject(sampleData, Formatting.Indented)
Zorgarath
jadi lebih mudah daripada mencoba melakukannya dengan tangan. Itu jadi rumit
ahong
26

Saya yakin ada cara-cara yang lebih baik untuk melakukan ini, tetapi di masa lalu saya telah menggunakan metode sesuatu seperti yang berikut ini untuk membuat cerita bersambung suatu objek menjadi string yang bisa saya catat:

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

Anda akan melihat bahwa metode ini mungkin juga mengembalikan pengecualian daripada objek serial, jadi Anda ingin memastikan bahwa objek yang ingin Anda log bisa serial.

Bernhard Hofmann
sumber
2
Mengingat fitur yang ditambahkan ke C # setelah Anda menjawab pertanyaan mungkin berguna untuk menunjukkan bahwa implementasi ini berfungsi dengan baik sebagai metode ekstensi. Jika diterapkan ke kelas Objek dan Anda merujuk ekstensi di mana pun Anda membutuhkannya, itu bisa menjadi cara yang mudah untuk memanggil fungsi.
Nikita G.
Saya terus menerima dari ini: Failed to access type 'System.__ComObject' failed. Noob to c #, akan sangat menghargai bantuan.
GuySoft
1
@ GuySoft Saya menduga salah satu properti pada objek Anda, atau objek itu sendiri, tidak serial.
Bernhard Hofmann
Sayangnya, Anda tidak dapat menggunakan metode ini di kelas tanpa konstruktor tanpa parameter. Mereka tidak serial.
Jarekczek
22

Anda bisa menggunakan Visual Studio Immediate Window

Cukup tempel ini (ubah actualke nama objek Anda dengan jelas):

Newtonsoft.Json.JsonConvert.SerializeObject(actual);

Seharusnya mencetak objek dalam JSON masukkan deskripsi gambar di sini

Anda harus dapat menyalinnya di atas alat teks mekanis atau notepad ++ dan mengganti tanda kutip ( \") dengan "dan baris baru ( \r\n) dengan ruang kosong, lalu menghapus tanda kutip ganda ( ") dari awal dan akhir dan menempelkannya ke jsbeautifier agar lebih mudah dibaca.

PEMBARUAN untuk komentar OP

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

ini akan memungkinkan Anda untuk membuang objek apa pun.

Semoga ini menghemat waktu Anda.

Matas Vaitkevicius
sumber
Terima kasih. Mungkin Anda tidak menangkapnya di pertanyaan awal saya, tetapi saya menunjukkan bahwa saya sudah tahu tentang jendela langsung, dan saya ingin melakukan hal yang sama ketika masuk ke aplikasi saya.
Dan Esparza
@DanEsparza Console.Log(Newtonsoft.Json.JsonConvert.SerializeObject(actual));? :) dan ya saya memang melewatkannya. Pertanyaan ini muncul ketika Anda mencari di google.co.uk/...
Matas Vaitkevicius
2
FYI, ketika Anda memiliki string JSON dalam string C #, klik ikon spyglass di sebelah kanan string dan pilih Text Visualizer. Ini akan memunculkan jendela yang menampilkan versi teks sederhana dari string JSON (tidak lolos tanda kutip atau \ r \ n).
Walter
17

ServiceStack.Text memiliki metode ekstensi T.Dump () yang melakukan hal ini, secara rekursif membuang semua properti jenis apa pun dalam format yang mudah dibaca.

Contoh penggunaan:

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

dan output:

{
    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
    }
}
mitos
sumber
1
Itu tidak berfungsi untuk bidang. OP secara eksplisit bertanya tentang "seluruh objek".
Konrad Morawski
5
He didn't say fields- Katanya entire objects, yang termasuk bidang. Dia juga menyebutkan fitur Jendela Segera Visual Studio sebagai contoh apa yang ingin dia capai ( "Hanya melakukan yang sederhana ? objectnameakan memberi saya 'dump' diformat dengan baik dari objek" ). ? objectnamemencetak semua bidang juga. This has been immensely helpful - one of my most used extension methods to date- Saya tidak mempertanyakan apakah ini berguna, hanya saja ia membuang seluruh objek.
Konrad Morawski
3
@KonradMorawski Seluruh objek yang keliru berarti dump rekursif dari objek, BUKAN mencakup bidang, yang dapat dengan mudah menyebabkan loop rekursif tak terbatas. Anda tidak boleh berasumsi apa yang disiratkan orang lain. Jawaban saya relevan dan membantu, suara turun Anda + komentar tidak.
mythz
1
@mythz ya tentu saja Anda perlu mencegah stack overflow (mis. setiap Int32bidang memiliki MaxValuebidang, yang merupakan Int32dirinya sendiri ...), itu poin yang baik, tetapi itu tidak mengubah fakta bahwa objek - dan tentu saja keseluruhan - terdiri dari bidang, juga, bukan hanya properti. Terlebih lagi (Anda tidak membahasnya), ? objectnamedi kolom is Immediate Window does display - tanpa memicu loop tak terbatas. Jika itu tentang downvote saya, saya dapat menariknya (jika Anda membiarkan saya dengan membukanya, itu). Saya pada dasarnya tidak setuju.
Konrad Morawski
4
-1 untuk dasarnya hanya tautan-jawaban, meskipun tampak hebat jika saya bisa menggunakannya! Mungkin saya buta, tetapi saya tidak dapat menemukan sumber melalui tautan itu; dua folder unggahan kosong. Apakah kode terlalu panjang untuk dimasukkan dalam jawaban?
14

Berikut ini adalah cara sederhana yang bodoh untuk menulis objek datar, yang diformat dengan baik:

using Newtonsoft.Json.Linq;

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

Apa yang terjadi adalah bahwa objek dikonversi terlebih dahulu ke representasi internal JSON oleh JObject.FromObject, dan kemudian dikonversi ke string JSON oleh ToString. (Dan tentu saja string JSON adalah representasi yang sangat bagus dari objek sederhana, terutama karena ToStringakan menyertakan baris baru dan indentasi.) "ToString" tentu saja asing (seperti yang tersirat dengan menggunakan +untuk menggabungkan string dan objek), tetapi Saya agak suka menentukannya di sini.

Hot Licks
sumber
5
JsonConvert.SerializeObject (apprec, Formatting.Indented) untuk kenyamanan membaca di log
Tertium
1
HotLicks - Saya ingin menyampaikan kepada Anda betapa pentingnya kontribusi ini bagi saya saat ini. Saya memiliki persyaratan untuk memberikan audit tentang apa yang berubah selama pembaruan dan Anda baru saja mengalihkan stres dari tingkat 'panik' kembali ke tingkat 'khawatir' yang dapat dikelola. Terima kasih tuan, semoga Anda memiliki kehidupan yang sangat panjang dan diberkati
Iofacture
4

Anda bisa menggunakan refleksi dan loop melalui semua properti objek, lalu dapatkan nilainya dan menyimpannya ke log. Pemformatan benar-benar sepele (Anda dapat menggunakan \ t untuk membuat indentasi properti objek dan nilainya):

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

Apa yang saya suka lakukan adalah mengganti ToString () sehingga saya mendapatkan hasil yang lebih berguna di luar nama tipe. Ini berguna dalam debugger, Anda dapat melihat informasi yang Anda inginkan tentang suatu objek tanpa perlu mengembangkannya.

Darryl Braaten
sumber
3

Saya menemukan perpustakaan bernama ObjectPrinter yang memungkinkan untuk dengan mudah membuang objek dan koleksi ke string (dan banyak lagi). Itu tepat seperti yang saya butuhkan.

Marek Dzikiewicz
sumber
3

Berikut ini adalah versi lain yang melakukan hal yang sama (dan menangani properti bersarang), yang saya pikir lebih sederhana (tidak ada ketergantungan pada pustaka eksternal dan dapat dimodifikasi dengan mudah untuk melakukan hal-hal selain penebangan):

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"));
    }
}
tenaga mesin
sumber
1
ini akan mati mengerikan jika Anda memiliki Dateproperti di objek batin Anda ... hanya mengatakan ...
Noctis
2

Anda dapat menulis metode WriteLine Anda sendiri-

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

Gunakan seperti-

WriteLine(myObject);

Untuk menulis koleksi kita bisa menggunakan-

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

Metode ini mungkin terlihat seperti-

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

Menggunakan if, else ifdan memeriksa antarmuka, atribut, tipe dasar, dll. Dan rekursi (karena ini adalah metode rekursif) dengan cara ini kita dapat mencapai objek dumper, tetapi pasti membosankan. Menggunakan objek dumper dari Sampel LINQ Microsoft akan menghemat waktu Anda.

Islam sejati
sumber
Karena penasaran: Bagaimana cara menangani array atau daftar ini? Atau properti yang mereferensikan objek induk?
Dan Esparza
@DanEsparza Terima kasih untuk menunjukkan kepada saya cara untuk lebih spesifik.
Ariful Islam
2

Berdasarkan jawaban @engineforce, saya membuat kelas ini yang saya gunakan dalam proyek PCL dari Solusi 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;
    }
}
gianlucaparadise
sumber
0

Semua jalur di atas menganggap bahwa objek Anda dapat serial ke XML atau JSON,
atau Anda harus mengimplementasikan solusi Anda sendiri.

Tetapi pada akhirnya Anda masih sampai pada titik di mana Anda harus menyelesaikan masalah seperti

  • rekursi pada objek
  • objek non-serializable
  • pengecualian
  • ...

Ditambah log Anda ingin informasi lebih lanjut:

  • ketika acara itu terjadi
  • callstack
  • yang threead
  • apa yang ada di sesi web
  • alamat ip mana
  • url
  • ...

Ada solusi terbaik yang menyelesaikan semua ini dan banyak lagi.
Gunakan paket Nuget ini: Desharp .
Untuk semua jenis aplikasi - aplikasi web dan desktop .
Lihat dokumentasi Desharp Github . Ini memiliki banyak opsi konfigurasi .

Panggil saja di mana saja:

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
  • itu dapat menyimpan log dalam HTML yang bagus (atau dalam format TEXT, dapat dikonfigurasi)
  • dimungkinkan untuk menulis secara opsional di utas latar (dapat dikonfigurasi)
  • ia memiliki opsi untuk kedalaman objek maksimal dan panjang string maksimal (dapat dikonfigurasi)
  • ia menggunakan loop untuk objek yang dapat diubah dan refleksi ke belakang untuk segala sesuatu yang lain,
    memang untuk apa pun yang dapat Anda temukan di lingkungan .NET .

Saya percaya ini akan membantu.

Tom Flídr
sumber