Каков наилучший способ сбрасывать все объекты в журнал на С#?

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

? objectname

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

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

Ответ 1

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

Ответ 2

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

Ответ 3

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

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

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

Ответ 4

У меня есть метод расширения 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
    }
}

Ответ 5

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

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

Newtonsoft.Json.JsonConvert.SerializeObject(actual);

Он должен напечатать объект в JSON enter image description here

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

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

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

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

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

Ответ 6

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

using Newtonsoft.Json.Linq;

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

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

Ответ 7

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

MyObject
    Property1 = value
    Property2 = value2
    OtherObject
       OtherProperty = value ...

Ответ 8

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

Ответ 9

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

Ответ 10

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

Ответ 11

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

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