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

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

? objectname

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

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

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

Dan Esparza 01.12.2015 16:23

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

Jesse Good 06.12.2015 04:20

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

Nick Westgate 17.03.2020 07:17
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
142
3
135 942
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Вы можете использовать отражение и перебрать все свойства объекта, затем получить их значения и сохранить их в журнале. Форматирование действительно тривиально (вы можете использовать \ t для отступа свойств объекта и его значений):

MyObject
    Property1 = value
    Property2 = value2
    OtherObject
       OtherProperty = value ...
Ответ принят как подходящий

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

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

Konrad Morawski 20.09.2012 10:57
пакет nuget для ObjectDumper теперь доступен. Он также предоставляет методы расширения DumpToString и Dump до класса Object. Удобно.
IsmailS 17.06.2015 12:43
w3wp.exe вылетает, когда я пытаюсь использовать ObjectDumper как Request.DumpToString("aaa");
Paul 02.05.2016 18:39

Я уверен, что есть более эффективные способы сделать это, но в прошлом я использовал метод, похожий на следующий, чтобы сериализовать объект в строку, которую я могу зарегистрировать:

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

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

В свете функций, добавленных в C# после того, как вы ответили на вопрос, может быть полезно отметить, что эта реализация прекрасно работает как метод расширения. Если применить его к классу Object и вы будете ссылаться на расширение везде, где оно вам нужно, это может быть удобным способом вызова функции.

Nikita G. 10.11.2013 13:14

Я получаю от этого: Failed to access type 'System.__ComObject' failed. Noob to C#, был бы признателен за помощь.

GuySoft 18.10.2014 22:50

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

Bernhard Hofmann 20.10.2014 13:15

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

Jarekczek 02.09.2016 16:14

Мне нравится переопределять ToString (), чтобы получить более полезный вывод помимо имени типа. Это удобно в отладчике, вы можете видеть нужную информацию об объекте, не расширяя ее.

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

Это не работает для полей. OP явно спрашивал о «целых объектах».

Konrad Morawski 20.09.2012 11:04
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 - я не сомневаюсь, что он полезен, только то, что он сбрасывает целые объекты.
Konrad Morawski 20.09.2012 12:24

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

mythz 20.09.2012 13:08

@mythz да, конечно, вам нужно предотвратить переполнение стека (например, каждое поле Int32 имеет поле MaxValue, которое само является Int32 ...), это хороший момент, но это не меняет того факта, что объекты - и конечно целые - тоже состоят из полей, а не только свойств. Более того (вы не обращались к этому), ? objectname в полях отображения Immediate Windowделает - без запуска бесконечного цикла. Если это касается моего отрицательного голоса, я могу его отозвать (если вы позволите мне, разблокировав его, то есть). Я все равно не согласен с этим в принципе.

Konrad Morawski 20.09.2012 13:33

@mythz, кстати, есть вероятность бесконечного цикла и в случае свойств, не так ли? Если ваш класс - public class Nested { public Nested Child { get; set; } }, вы можете сделать var root = new Nested(); root.Child = root;. Если я не понимаю вас как-то по этому поводу.

Konrad Morawski 20.09.2012 13:36

@KonradMorawski Вы можете предотвратить переполнение стека, поддерживая состояние, чего не делают большинство и быстрые сериализаторы. У вас больше шансов контролировать внешнюю форму вашего типа с помощью общедоступных свойств, чем с внутренними полями, которые добавляются без всяких забот.

mythz 20.09.2012 13:43

-1 за ответ, содержащий только ссылку, хотя он выглядел бы великолепно, если бы я мог его использовать! Возможно, я слепой, но я не могу найти источник по этой ссылке; две папки загрузки пусты. Код слишком длинный, чтобы включать его в ответ?

user565869 14.07.2014 23:26

Вот до глупости простой способ написать хорошо отформатированный плоский объект:

using Newtonsoft.Json.Linq;

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

Происходит то, что объект сначала преобразуется во внутреннее представление JSON с помощью JObject.FromObject, а затем преобразуется в строку JSON с помощью ToString. (И, конечно же, строка JSON - очень хорошее представление простого объекта, тем более, что ToString будет включать символы новой строки и отступы.) «ToString», конечно, не имеет значения (как это подразумевается при использовании + для соединения строки и объекта) , но я хотел бы указать это здесь.

JsonConvert.SerializeObject (признательность, Formatting.Indented) для удобного чтения в журнале

Tertium 24.08.2016 02:04

HotLicks - я хочу донести до вас, насколько важен для меня этот вклад прямо сейчас. У меня есть требование предоставить аудит того, что изменилось во время обновления, и вы только что перенесли мой стресс с уровня «паники» обратно на управляемый уровень «беспокойства». Спасибо, сэр, желаю вам долгой и счастливой жизни

Iofacture 17.05.2019 01:25

Для более крупного графа объектов я использую 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».

Screenshot of Visual Studio 2013 Locals window

Я использую Visual Studio 2013.

SerializeObj -> SerializeObject?

Wiseman 27.10.2014 15:07

Молодец, спасибо. Я не могу установить инструменты отладки для Visual Studio на моем удаленном сервере, и эта штука очень хорошо работает в моем приложении asp.net mvc.

Liam Kernighan 23.04.2018 20:45

Для хорошего форматирования можно использовать: Newtonsoft.Json.JsonConvert.SerializeObject(sampleData, Formatting.Indented)

Zorgarath 09.03.2019 08:55

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

ahong 17.10.2019 11:54

В .NET Core 3.1 и .NET 5+ вы также можете использовать готовый API System.Text.Json.JsonSerializer.Serialize(yourObject).

vulcan raven 18.09.2020 13:11

Вы можете использовать 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
    }
}

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

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

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

Dan Esparza 23.06.2015 18:38

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

Matas Vaitkevicius 23.06.2015 18:52

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

Walter 31.08.2016 04:00

Я нашел библиотеку под названием ObjectPrinter, которая позволяет легко сбрасывать объекты и коллекции в строки (и многое другое). Он делает именно то, что мне нужно.

Вы можете написать свой собственный метод 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 сэкономит ваше время.

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

Dan Esparza 01.03.2016 17:50

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

Ariful Islam 02.03.2016 09:33

Ниже приводится другая версия, которая делает то же самое (и обрабатывает вложенные свойства), которая, на мой взгляд, проще (нет зависимостей от внешних библиотек и может быть легко изменена для выполнения других действий, кроме ведения журнала):

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

это ужасно умрет, если у вас есть свойство Date в вашем внутреннем объекте ... просто говорю ...

Noctis 07.09.2017 07:43

Основываясь на ответе @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;
    }
}

Все указанные выше пути предполагают, что ваши объекты сериализуемы в 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.

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

Другие вопросы по теме