Как лучше всего выгружать целые объекты в журнал на C #?


129

Поэтому для просмотра текущего состояния объекта во время выполнения мне очень нравится то, что дает мне окно Visual Studio Immediate. Просто делаю простой

? objectname

Дает мне красиво отформатированный «дамп» объекта.

Есть ли простой способ сделать это в коде, чтобы я мог сделать что-то подобное при регистрации?


В конце концов, я довольно часто использовал T.Dump. Это довольно надежное решение - вам просто нужно быть осторожным с рекурсией.
Дэн Эспарза

Это старый вопрос, но он оказывается в первых рядах поисковых запросов. Для будущих читателей: посмотрите это на расширение . Отлично работал у меня в VS2015.
Джесси Гуд

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

Ответы:


55

Вы можете основывать что-нибудь на коде ObjectDumper, который поставляется с примерами Linq .
Также посмотрите ответ на этот связанный вопрос, чтобы получить образец.


5
Он также не работает для массивов (он просто отображает тип и длину массива, но не печатает его содержимое).
Конрад Моравски

5
Пакет nuget для ObjectDumper теперь доступен. Он также предоставляет метод расширения DumpToStringи Dumpв Objectклассе. Handy.
IsmailS

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 09

намного проще, чем пытаться сделать это вручную. Все усложняется
ахонг

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 to c #, был бы признателен за помощь.
GuySoft

1
@GuySoft Я подозреваю, что одно из свойств вашего объекта или сам объект не сериализуемы.
Бернхард Хофманн

К сожалению, вы не можете использовать этот метод в классах без конструктора без параметров. Они не сериализуемы.
Ярекчек 02

22

Вы можете использовать Visual Studio Immediate Window

Просто вставьте это ( actualочевидно, измените имя вашего объекта):

Newtonsoft.Json.JsonConvert.SerializeObject(actual);

Он должен печатать объект в JSON введите описание изображения здесь

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

ОБНОВЛЕНИЕ к комментарию OP

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

это должно позволить вам сбросить любой объект.

Надеюсь, это сэкономит вам время.


Спасибо. Возможно, вы не уловили этого в моем первоначальном вопросе, но я указал, что уже знаю о немедленном окне и хочу сделать то же самое при входе в свое приложение.
Дэн Эспарза

@DanEsparza Console.Log(Newtonsoft.Json.JsonConvert.SerializeObject(actual));? :) и да, я действительно это пропустил. Этот вопрос возникает при поиске на google.co.uk/…
Матас Вайткявичюс,

2
К вашему сведению, когда у вас есть строка JSON в строке C #, щелкните значок подзорной трубы справа от строки и выберите визуализатор текста. Откроется окно, в котором отображается текстовая версия строки JSON (без экранированных кавычек или \ r \ n).
Уолтер,

17

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, в том числе поля. Он также упомянул функцию Immediate Window в Visual Studio как пример того, чего он хотел достичь ( «Простое выполнение ? objectnameдаст мне красиво отформатированный« дамп »объекта» ). ? objectnameраспечатывает все поля. This has been immensely helpful - one of my most used extension methods to date- Я не сомневаюсь, что это полезно, только то, что он сбрасывает целые объекты.
Конрад Моравски

3
@KonradMorawski Неправильные целые объекты означают рекурсивный дамп объекта, а НЕ то, что он включает поля, что может легко привести к бесконечному рекурсивному циклу. Вы не должны предполагать то, что подразумевают другие. Мой ответ актуален и полезен, ваш голос + комментарий - нет.
мифз

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 (благодарность, 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свойство в вашем внутреннем объекте ... просто говорю ...
Ноктис

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 Sample сэкономит ваше время.


Из любопытства: как это обрабатывает массивы или списки? Или свойства, ссылающиеся на родительские объекты?
Дэн Эспарса,

@DanEsparza Спасибо, что показал мне способ быть более конкретным.
Ariful Islam 02

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 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 (или в текстовом формате, настраиваемый)
  • можно писать дополнительно в фоновом потоке (настраивается)
  • у него есть параметры для максимальной глубины объектов и максимальной длины строк (настраивается)
  • он использует циклы для повторяемых объектов и обратное отражение для всего остального,
    да и вообще для всего, что вы можете найти в среде .NET .

Я верю, что это поможет.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.