С#: печать всех свойств объекта
Есть ли встроенный метод .NET, который может записывать все свойства и такой объект в консоль? Мог бы сделать это с использованием отражения, конечно, но мне любопытно, если это уже существует... тем более, что вы можете сделать это в Visual Studio в окне Immediate. Там вы можете указать имя объекта (в режиме отладки), нажать enter и напечатать его довольно красиво со всеми его материалами.
Существует ли такой метод?
Ответы
Ответ 1
Известно, что класс ObjectDumper делает это. Я никогда не подтверждал, но я всегда подозревал, что это окно использует это окно.
EDIT: Я просто понял, что код для ObjectDumper на самом деле находится на вашей машине. Перейдите к:
c:/Программные файлы /Microsoft Visual Studio 9.0/Образцы/1033/CSharpSamples.zip
Это распакуется в папку LinqSamples. Там есть проект ObjectDumper. Используйте это.
(Это также сделает Дэвида в комментариях счастливым:))
Ответ 2
Для этого можно использовать класс TypeDescriptor:
foreach(PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj))
{
string name=descriptor.Name;
object value=descriptor.GetValue(obj);
Console.WriteLine("{0}={1}",name,value);
}
TypeDescriptor живет в пространстве имен System.ComponentModel и является API, который Visual Studio использует для отображения вашего объекта в своем браузере свойств. Это в конечном счете основано на отражении (как и любое решение), но оно обеспечивает довольно хороший уровень абстракции от API отражения.
Ответ 3
На основе ObjectDumper образцов LINQ я создал версию, которая сбрасывает каждое из свойств в своей собственной строке.
Этот образец класса
namespace MyNamespace
{
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
public IList<Hobby> Hobbies { get; set; }
}
public class Hobby
{
public string Name { get; set; }
}
public class Address
{
public string Street { get; set; }
public int ZipCode { get; set; }
public string City { get; set; }
}
}
имеет выход
{MyNamespace.User}
FirstName: "Arnold"
LastName: "Schwarzenegger"
Address: { }
{MyNamespace.Address}
Street: "6834 Hollywood Blvd"
ZipCode: 90028
City: "Hollywood"
Hobbies: ...
{MyNamespace.Hobby}
Name: "body building"
Вот код.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
public class ObjectDumper
{
private int _level;
private readonly int _indentSize;
private readonly StringBuilder _stringBuilder;
private readonly List<int> _hashListOfFoundElements;
private ObjectDumper(int indentSize)
{
_indentSize = indentSize;
_stringBuilder = new StringBuilder();
_hashListOfFoundElements = new List<int>();
}
public static string Dump(object element)
{
return Dump(element, 2);
}
public static string Dump(object element, int indentSize)
{
var instance = new ObjectDumper(indentSize);
return instance.DumpElement(element);
}
private string DumpElement(object element)
{
if (element == null || element is ValueType || element is string)
{
Write(FormatValue(element));
}
else
{
var objectType = element.GetType();
if (!typeof(IEnumerable).IsAssignableFrom(objectType))
{
Write("{{{0}}}", objectType.FullName);
_hashListOfFoundElements.Add(element.GetHashCode());
_level++;
}
var enumerableElement = element as IEnumerable;
if (enumerableElement != null)
{
foreach (object item in enumerableElement)
{
if (item is IEnumerable && !(item is string))
{
_level++;
DumpElement(item);
_level--;
}
else
{
if (!AlreadyTouched(item))
DumpElement(item);
else
Write("{{{0}}} <-- bidirectional reference found", item.GetType().FullName);
}
}
}
else
{
MemberInfo[] members = element.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance);
foreach (var memberInfo in members)
{
var fieldInfo = memberInfo as FieldInfo;
var propertyInfo = memberInfo as PropertyInfo;
if (fieldInfo == null && propertyInfo == null)
continue;
var type = fieldInfo != null ? fieldInfo.FieldType : propertyInfo.PropertyType;
object value = fieldInfo != null
? fieldInfo.GetValue(element)
: propertyInfo.GetValue(element, null);
if (type.IsValueType || type == typeof(string))
{
Write("{0}: {1}", memberInfo.Name, FormatValue(value));
}
else
{
var isEnumerable = typeof(IEnumerable).IsAssignableFrom(type);
Write("{0}: {1}", memberInfo.Name, isEnumerable ? "..." : "{ }");
var alreadyTouched = !isEnumerable && AlreadyTouched(value);
_level++;
if (!alreadyTouched)
DumpElement(value);
else
Write("{{{0}}} <-- bidirectional reference found", value.GetType().FullName);
_level--;
}
}
}
if (!typeof(IEnumerable).IsAssignableFrom(objectType))
{
_level--;
}
}
return _stringBuilder.ToString();
}
private bool AlreadyTouched(object value)
{
if (value == null)
return false;
var hash = value.GetHashCode();
for (var i = 0; i < _hashListOfFoundElements.Count; i++)
{
if (_hashListOfFoundElements[i] == hash)
return true;
}
return false;
}
private void Write(string value, params object[] args)
{
var space = new string(' ', _level * _indentSize);
if (args != null)
value = string.Format(value, args);
_stringBuilder.AppendLine(space + value);
}
private string FormatValue(object o)
{
if (o == null)
return ("null");
if (o is DateTime)
return (((DateTime)o).ToShortDateString());
if (o is string)
return string.Format("\"{0}\"", o);
if (o is char && (char)o == '\0')
return string.Empty;
if (o is ValueType)
return (o.ToString());
if (o is IEnumerable)
return ("...");
return ("{ }");
}
}
и вы можете использовать его так:
var dump = ObjectDumper.Dump(user);
Изменить
- Двунаправленные ссылки теперь прекращены. Поэтому HashCode объекта хранится в списке.
- Уже исправлено (см. комментарии)
- FormatValue исправлено (см. комментарии)
Ответ 4
Возможно через JavaScriptSerializer.Serialize?
Ответ 5
Относительно TypeDescriptor от ответа Sean (я не могу комментировать, потому что у меня плохая репутация)... одно преимущество использования TypeDescriptor над GetProperties() заключается в том, что TypeDescriptor имеет механизм динамического добавления свойств к объектам во время выполнения и нормального отражения пропустит их.
Например, при работе с PowerShell PSObject, который может иметь свойства и методы, добавленные во время выполнения, они реализовали собственный TypeDescriptor, который объединяет эти элементы со стандартным набором элементов. Используя TypeDescriptor, ваш код не должен знать об этом факте.
Компоненты, элементы управления, и я думаю, что, возможно, DataSets также используют этот API.
Ответ 6
Следующий фрагмент сделает желаемую функцию:
Type t = obj.GetType();//where obj is object whose properties you need.
PropertyInfo [] pi =t.GetProperties();
foreach (PropertyInfo p in pi)
{
System.Console.WriteLine(p.Name + " " + p.GetType);
}
Я думаю, что если вы напишете это как метод расширения, вы можете использовать его для всех типов объектов.
Ответ 7
Это именно то, о чем идет речь. Я не думаю, что там есть более простое решение, но в любом случае отражение не настолько интенсивно для кода.
Ответ 8
Не думай так. Мне всегда приходилось писать их или использовать кто-то другой, чтобы получить эту информацию. Должен быть отражением, насколько мне известно.
EDIT:
Проверьте этот вариант. Я изучал некоторые отладки на длинных графах объектов и заметил это, когда я добавляю часы, VS бросает в этом классе: Mscorlib_CollectionDebugView<>
. Это внутренний тип для удобного отображения коллекций для просмотра в режимах отладки окна/кода. Теперь coz это внутреннее вы можете ссылаться на него, но вы можете использовать Reflector для копирования (из mscorlib) кода и иметь свой собственный (ссылка выше имеет пример копирования/вставки). Выглядит очень полезно.
Ответ 9
Любое другое решение/библиотека в конце концов будет использовать отражение для интроспекции типа...