C#:打印对象的所有属性

有没有一种方法可以将.NET的所有属性和对象写入控制台? 当然可以使用reflection,但我很好奇,如果这已经存在…特别是因为你可以在Visual Studio中立即窗口中。 在那里你可以有一个对象的名字(在debugging模式下),按下回车键,它会打印出所有的东西。

像这样的方法存在吗?

ObjectDumper类已经知道这样做了。 我从来没有证实,但我一直怀疑,直接窗口使用。

编辑:我只是意识到,ObjectDumper的代码实际上是在你的机器上。 去:

c:/ Program Files / Microsoft Visual Studio 9.0 / Samples / 1033 / CSharpSamples.zip

这将解压缩到一个名为LinqSamples的文件夹。 在那里,有一个名为ObjectDumper的项目。 使用它。

(这也将使大卫在评论中开心:))

您可以使用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。 它最终基于reflection(就像任何解决scheme一样),但它从reflectionAPI提供了相当好的抽象级别。

基于LINQ示例的ObjectDumper,我创build了一个版本,它可以转储每一个属性。

这个类样本

 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(请参阅评论)

也许通过JavaScriptSerializer.Serialize ?

关于Sean的答复中的TypeDescriptor(我不能评论,因为我有一个不好的名声)…使用TypeDescriptor比GetProperties()更好的一个好处是TypeDescriptor有一个机制dynamic附加属性的对象在运行时和正常的reflection将错过这些。

例如,当使用PowerShell的PSObject(可以在运行时添加属性和方法)时,他们实现了一个自定义的TypeDescriptor,它将这些成员与标准成员集合并在一起。 通过使用TypeDescriptor,你的代码不需要知道这个事实。

组件,控件,我想也许DataSets也使用这个API。

以下代码将执行所需的function:

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

我想如果你把它写成扩展方法,你可以在所有types的对象上使用它。

这正是reflection的意思。 我不认为有一个更简单的解决scheme,但reflection并不是代码密集型的。

任何其他的解决scheme/库最终将使用reflection来反思types…

不要这样想。 我一直不得不写他们或使用别人的工作来获取这些信息。 就我所知,必须进行反思。

编辑:
看看这个 。 我正在调查长对象图上的一些debugging,并注意到这一点,当我添加手表,VS引发这个类: Mscorlib_CollectionDebugView<> 。 这是一种内部types,用于在监视窗口/代码debugging模式下很好地显示集合。 现在因为它是内部的,你可以引用它,但是你可以使用Reflector从(从mscorlib)复制代码,并有自己的(上面的链接有一个复制/粘贴的例子)。 看起来非常有用。