Поэтому для просмотра текущего состояния объекта во время выполнения мне очень нравится то, что дает мне окно Visual Studio Immediate. Просто делаю простой
? objectname
Дает мне красиво отформатированный «дамп» объекта.
Есть ли простой способ сделать это в коде, чтобы я мог сделать что-то подобное при регистрации?
Это старый вопрос, но он выходит на первый план во многих поисковых запросах. Для будущих читателей: см. это против расширения. Отлично работал у меня в VS2015.
Обновление на 2020 год, поскольку этот плагин VS не поддерживается и не имеет некоторых функций. Следующая библиотека делает то же самое в коде - и у нее есть несколько дополнительных функций, например он отслеживает, где он уже был, чтобы избежать петель: github.com/thomasgalliker/ObjectDumper





Вы можете использовать отражение и перебрать все свойства объекта, затем получить их значения и сохранить их в журнале. Форматирование действительно тривиально (вы можете использовать \ t для отступа свойств объекта и его значений):
MyObject
Property1 = value
Property2 = value2
OtherObject
OtherProperty = value ...
Вы можете основывать что-то на коде ObjectDumper, который поставляется с Примеры Linq.
Также посмотрите ответ этого связанный вопрос, чтобы получить образец.
Он также не работает для массивов (он просто отображает тип и длину массива, но не печатает его содержимое).
DumpToString и Dump до класса Object. Удобно.
w3wp.exe вылетает, когда я пытаюсь использовать ObjectDumper как Request.DumpToString("aaa");Я уверен, что есть более эффективные способы сделать это, но в прошлом я использовал метод, похожий на следующий, чтобы сериализовать объект в строку, которую я могу зарегистрировать:
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 и вы будете ссылаться на расширение везде, где оно вам нужно, это может быть удобным способом вызова функции.
Я получаю от этого: Failed to access type 'System.__ComObject' failed. Noob to C#, был бы признателен за помощь.
@GuySoft Я подозреваю, что одно из свойств вашего объекта или сам объект не сериализуемы.
К сожалению, вы не можете использовать этот метод в классах без конструктора без параметров. Они не сериализуемы.
Мне нравится переопределять 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 явно спрашивал о «целых объектах».
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 - я не сомневаюсь, что он полезен, только то, что он сбрасывает целые объекты.
@KonradMorawski Неправильно целые объекты означает рекурсивный дамп объекта, а НЕ то, что он включает поля, что может легко привести к бесконечному рекурсивному циклу. Вы не должны предполагать то, что намекают другие. Мой ответ актуален и полезен, ваш голос + комментарий - нет.
@mythz да, конечно, вам нужно предотвратить переполнение стека (например, каждое поле Int32 имеет поле MaxValue, которое само является Int32 ...), это хороший момент, но это не меняет того факта, что объекты - и конечно целые - тоже состоят из полей, а не только свойств. Более того (вы не обращались к этому), ? objectname в полях отображения Immediate Windowделает - без запуска бесконечного цикла. Если это касается моего отрицательного голоса, я могу его отозвать (если вы позволите мне, разблокировав его, то есть). Я все равно не согласен с этим в принципе.
@mythz, кстати, есть вероятность бесконечного цикла и в случае свойств, не так ли? Если ваш класс - public class Nested { public Nested Child { get; set; } }, вы можете сделать var root = new Nested(); root.Child = root;. Если я не понимаю вас как-то по этому поводу.
@KonradMorawski Вы можете предотвратить переполнение стека, поддерживая состояние, чего не делают большинство и быстрые сериализаторы. У вас больше шансов контролировать внешнюю форму вашего типа с помощью общедоступных свойств, чем с внутренними полями, которые добавляются без всяких забот.
-1 за ответ, содержащий только ссылку, хотя он выглядел бы великолепно, если бы я мог его использовать! Возможно, я слепой, но я не могу найти источник по этой ссылке; две папки загрузки пусты. Код слишком длинный, чтобы включать его в ответ?
Вот до глупости простой способ написать хорошо отформатированный плоский объект:
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) для удобного чтения в журнале
HotLicks - я хочу донести до вас, насколько важен для меня этот вклад прямо сейчас. У меня есть требование предоставить аудит того, что изменилось во время обновления, и вы только что перенесли мой стресс с уровня «паники» обратно на управляемый уровень «беспокойства». Спасибо, сэр, желаю вам долгой и счастливой жизни
Для более крупного графа объектов я использую 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.
SerializeObj -> SerializeObject?
Молодец, спасибо. Я не могу установить инструменты отладки для Visual Studio на моем удаленном сервере, и эта штука очень хорошо работает в моем приложении asp.net mvc.
Для хорошего форматирования можно использовать: Newtonsoft.Json.JsonConvert.SerializeObject(sampleData, Formatting.Indented)
намного проще, чем пытаться сделать это вручную. Это усложняется
В .NET Core 3.1 и .NET 5+ вы также можете использовать готовый API System.Text.Json.JsonSerializer.Serialize(yourObject).
Вы можете использовать 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/…
К вашему сведению, когда у вас есть строка JSON в строке C#, щелкните значок подзорной трубы справа от строки и выберите визуализатор текста. Откроется окно, в котором отображается текстовая версия строки JSON (без экранированных кавычек или \ r \ n).
Я нашел библиотеку под названием 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 сэкономит ваше время.
Из любопытства: как это обрабатывает массивы или списки? Или свойства, ссылающиеся на родительские объекты?
@DanEsparza Спасибо, что показал мне способ быть более конкретным.
Ниже приводится другая версия, которая делает то же самое (и обрабатывает вложенные свойства), которая, на мой взгляд, проще (нет зависимостей от внешних библиотек и может быть легко изменена для выполнения других действий, кроме ведения журнала):
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 в вашем внутреннем объекте ... просто говорю ...
Основываясь на ответе @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,
или вы должны реализовать собственное решение.
Но в конце концов вы все равно дойдете до того, что вам придется решать такие проблемы, как
Плюс журнал, вам нужна дополнительная информация:
Есть лучшее решение, которое решает все эти и многие другие проблемы.
Используйте этот пакет 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
Я верю, что это поможет.
В конце концов, я довольно часто использовал T.Dump. Это довольно надежное решение - вам просто нужно быть осторожным с рекурсией.