Как лучше всего выгружать целые объекты в журнал на 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
    }
}
mythz
источник
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"));
    }
}
engineforce
источник
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;
    }
}
gianlucaparadise
источник
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 .

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

Том Флидр
источник