Який найкращий спосіб скинути цілі об’єкти в журнал на C #?


129

Тож за перегляд стану поточного об’єкта під час виконання мені дуже подобається те, що дає мені вікно Visual Studio Neposredno. Просто роби просто

? objectname

Дасть мені добре відформатований 'дамп' об’єкта.

Чи є простий спосіб зробити це в коді, щоб я міг зробити щось подібне під час входу в систему?


Врешті-решт, я досить широко використав T.Dump. Це досить солідне рішення - просто потрібно бути обережним щодо рекурсії.
Дан Еспарца

Це давнє запитання, але воно з'являється у верхній частині багатьох пошукових звернень. Для читачів в майбутньому: Дивіться це проти розширення . Для мене чудово працював у VS2015.
Джессі Хороший

1
Оновлення до 2020 року, оскільки плагін VS не підтримується і не має деяких функцій. Наступна бібліотека робить те саме в коді - і вона має кілька додаткових функцій, наприклад, вона відстежує місця, де вона вже відвідана, щоб уникнути циклів: github.com/thomasgalliker/ObjectDumper
Нік Вестгейт,

Відповіді:


55

Ви можете базувати щось на коді ObjectDumper, який постачається із зразками Linq .
Подивіться також відповідь на це пов'язане питання, щоб отримати вибірку.


5
Він також не працює для масивів (він просто відображає тип і довжину масиву, але не друкує його вміст).
Конрад Моравський

5
Новий пакет для ObjectDumper тепер доступний. Він також надає метод розширення DumpToStringі Dumpв Objectкласі. Зручно.
ІсмаїлС

2
w3wp.exeаварії, коли я намагаюся використовувати, ObjectDumperякRequest.DumpToString("aaa");
Павло

60

Для більшого об'єктного графа я використовую Json, але з дещо іншою стратегією. По-перше, у мене є статичний клас, який легко викликати і статичним методом, який завершує перетворення 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?
Wiseman

Блискуче, дякую. Я не можу встановити інструменти налагодження для Visual Studio на віддалений сервер, і ця річ надзвичайно добре працює в моєму додатку asp.net mvc.
Ліам Керніган

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 і ви посилаєте на розширення скрізь, де воно вам потрібно, це може бути зручним способом викликати функцію.
Микита Г.

Я отримую від цього: Failed to access type 'System.__ComObject' failed. Noob до c #, буде вдячний за допомогу.
GuySoft

1
@GuySoft Я підозрюю, що одне з властивостей вашого об'єкта або сам об'єкт не може бути серіалізаційним.
Бернхард Хофманн

На жаль, ви не можете використовувати цей метод на класах без конструктора без параметрів, на жаль. Вони не піддаються серіалізації.
Jarekczek

22

Ви можете використовувати негайне вікно Visual Studio

Просто вставте це ( actualочевидно, змініть ім'я об'єкта):

Newtonsoft.Json.JsonConvert.SerializeObject(actual);

Він повинен надрукувати об'єкт у JSON введіть тут опис зображення

Ви повинні мати змогу скопіювати його через текстмеханічний текстовий інструмент або блокнот ++ та замінити пропущені лапки ( \") "та нові рядки ( \r\n) на порожній простір, а потім видалити подвійні лапки ( ") з початку та в кінці та вставити їх у jsbeautifier, щоб зробити його більш читабельним.

ОНОВЛЕННЯ до коментаря ОП

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

це повинно дозволяти скидати будь-який об’єкт.

Сподіваюсь, це заощадить певний час.


Дякую. Можливо, ви не впіймали це в моєму первісному запитанні, але я вказав, що я вже знав про безпосереднє вікно, і хотів зробити те саме, коли входив у свій додаток.
Dan Esparza

@DanEsparza Console.Log(Newtonsoft.Json.JsonConvert.SerializeObject(actual));? :) і так, я дійсно пропустив це. Це запитання виникає під час пошуку google.co.uk/…
Matas Vaitkevicius

2
FYI, якщо у вас є рядок JSON в рядку C #, натисніть на значок шпигуна праворуч від рядка та виберіть візуалізатор тексту. Він відобразить вікно, яке показує звичайну текстову версію рядка 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
Це не працює для полів. В ОП явно питали про "цілі об'єкти".
Конрад Моравський

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- Я не сумніваюся, що це корисно, тільки що він скидає цілі предмети.
Конрад Моравський

3
@KonradMorawski Неправильні цілі об'єкти означають рекурсивне скидання об’єкта, НЕ, що воно включає поля, які можуть легко призвести до нескінченного рекурсивного циклу. Не слід вважати, що мають на увазі інші. Моя відповідь є релевантною та корисною, ваш голос "проти" + коментар - ні.
mythz

1
@mythz так, звичайно, ви повинні запобігти переповненню стека (наприклад, у кожному Int32полі є MaxValueполе, яке саме по Int32собі ...), це хороший момент, але це не змінює факту, що об'єкти - і, звичайно, цілі - теж складаються з полів, а не лише властивостей. Більш того (ви не адреса, один), ? objectnameв Immediate Window робить полів відображення - не викликаючи нескінченний цикл. Якщо це стосується мого потоку, я можу зняти його (якщо ви дозволите, розблокувавши його, тобто). Я в принципі не згоден.
Конрад Моравський

4
-1 для відповіді, що стосується лише посилання, хоча це чудово виглядає, якщо я міг би її використати! Можливо, я сліпий, але не можу знайти джерело через це посилання; дві папки для завантаження порожні. Чи код занадто довгий для включення у відповідь?

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 - Я хочу передати вам, наскільки важливий цей внесок для мене зараз. У мене є вимога надати аудит того, що змінилося під час оновлення, і ви щойно перейняли мій стрес від "панічного" рівня до рівня керованого рівня "занепокоєння". Дякую, сер, нехай у вас буде дуже довге і благословенне життя
Іофактура

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властивість у вашому внутрішньому об’єкті ... просто кажу ...
Noctis

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та перевіряючи інтерфейси, атрибути, базовий тип тощо та рекурсію (оскільки це рекурсивний метод) таким чином, ми можемо досягти об'єкта-самоскида, але це нудно точно. Використання об'єкта-самоскида з зразка LINQ Microsoft врятує ваш час.


З цікавості: як це обробляє масиви чи списки? Або властивості, на які посилаються батьківські об'єкти?
Дан Еспарца

@DanEsparza Спасибі, що показав мені спосіб бути більш конкретним.
Аріол Іслам

2

На основі відповіді @engineforce я створив цей клас, який використовую в проекті PCL рішення 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;
    }
}

0

Усі вищевказані шляхи припускають, що ваші об'єкти можна серіалізувати до XML або JSON,
або ви повинні реалізувати власне рішення.

Але врешті-решт, ви все-таки дістанетесь до того, коли вам доведеться вирішувати подібні проблеми

  • рекурсія в об’єктах
  • об'єкти, що не піддаються серіалізації
  • винятки
  • ...

Плюс журналу ви хочете отримати додаткову інформацію:

  • коли подія сталася
  • виклик
  • яка тріада
  • що було на веб-сесії
  • яка IP-адреса
  • URL
  • ...

Є найкраще рішення, яке вирішує все це та багато іншого.
Скористайтеся цим пакетом Nuget : Desharp .
Для всіх типів програм - як веб-так і настільних додатків .
Перегляньте це документацію Дешарпа Гітбуба . У ньому багато варіантів конфігурації .

Просто зателефонуйте куди завгодно:

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 .

Я вірю, що це допоможе.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.