是否有一个内置于.NET的方法可以将所有属性和对象写入控制台?当然可以使用反射制作一个,但我很好奇,如果这已经存在...特别是因为你可以在立即窗口中的Visual Studio中完成它。在那里你可以有一个对象名称(在调试模式下),按回车键,它的打印效果非常好。
这样的方法是否存在?
答案 0 :(得分:283)
您可以使用 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 命名空间中,是Visual Studio用于在其属性浏览器中显示对象的API。它最终基于反射(就像任何解决方案一样),但它从反射API提供了相当好的抽象级别。
答案 1 :(得分:92)
基于LINQ示例的ObjectDumper,我创建了一个版本,可以在自己的行上转储每个属性。
此课程样本
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);
修改
答案 2 :(得分:60)
已知ObjectDumper类可以做到这一点。我从来没有确认过,但我一直怀疑即时窗口会使用它。
编辑:我刚才意识到,ObjectDumper的代码实际上在你的机器上。转到:
c:/ Program Files / Microsoft Visual Studio 9.0 / Samples / 1033 / CSharpSamples.zip
这将解压缩到名为LinqSamples的文件夹。在那里,有一个名为ObjectDumper的项目。用那个。
(这也会让评论中的大卫高兴:))
答案 3 :(得分:23)
答案 4 :(得分:6)
关于Sean回复的TypeDescriptor(由于名声不好,我无法发表评论)...使用TypeDescriptor优于GetProperties()的一个优点是TypeDescriptor有一种机制,可以在运行时动态地将属性附加到对象并进行正常反射会错过这些。
例如,当使用PowerShell的PSObject(可以在运行时添加属性和方法)时,它们实现了一个自定义TypeDescriptor,它将这些成员与标准成员集合并在一起。通过使用TypeDescriptor,您的代码不需要知道这一事实。
组件,控件,我认为DataSet也可以使用这个API。
答案 5 :(得分:5)
以下代码段将执行所需的功能:
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());
}
我认为如果你把它写成扩展方法,你可以在所有类型的对象上使用它。
答案 6 :(得分:1)
这正是反射的意思。我不认为有一个更简单的解决方案,但无论如何反射都不是代码密集型。
答案 7 :(得分:0)
不要这么认为。我总是不得不写他们或使用别人的工作来获取这些信息。据我所知,必须进行反思。
修改强>
检查this out。我正在调查一些关于长对象图的调试,并在我添加Watches时注意到这一点,VS在这个类中抛出:Mscorlib_CollectionDebugView<>
。它是一种内部类型,可以很好地显示集合,以便在监视窗口/代码调试模式下查看。现在因为它是内部的你可以引用它,但你可以使用Reflector来复制(从mscorlib)代码并拥有自己的(上面的链接有一个复制/粘贴示例)。看起来非常有用。
答案 8 :(得分:0)
任何其他解决方案/库最终都会使用反射来反省类型...