C#:打印对象的所有属性[重复]
Posted
技术标签:
【中文标题】C#:打印对象的所有属性[重复]【英文标题】:C#: Printing all properties of an object [duplicate] 【发布时间】:2010-10-25 12:42:11 【问题描述】:.NET 中是否有一种方法可以将对象的所有属性等写入控制台?
当然可以使用反射,但我很好奇这是否已经存在......尤其是因为您可以在 Visual Studio 中的即时窗口中做到这一点。在那里你可以输入一个对象名称(在调试模式下),按回车键,它的所有内容都会被打印得相当漂亮。
这样的方法存在吗?
【问题讨论】:
这个问题的答案比What is the best way to dump entire objects to a log in C#?上要好 “原始”问题中的最高答案指向这个问题。顺序错了。 这里的对象转储器和其他反射答案是什么...序列化程序不会实现这一点吗? 【参考方案1】:您可以使用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 提供了相当好的抽象级别。
【讨论】:
酷!不知道那件事。与使用 obj.GetType().GetProperties() 和 GetValue 和 SetValue 相比,如何使用这个 PropertyDescriptor 和 GetValue?只是“界面”不同而已吗? 它是反射 API 之上的高级 API。它旨在以用户友好的方式显示属性。 PropertyDescriptor 类有多种方法可以让您轻松编辑、更改和重置属性值,如果您愿意的话。 在你的答案中提到命名空间做得很好! 如果您有一个具有嵌套值的复杂对象,则此答案完全没用。这只持续到 1 级,它根本不会更进一步...... @Best_Where_Gives - 所以你可以扩展代码来处理这个问题,在 engineforce 已经完成了。有时您必须自己编写一些代码..!【参考方案2】:跟随 sn -p 将执行所需的功能:
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.GetValue(obj));
我认为如果你把它写成扩展方法,你可以在所有类型的对象上使用它。
【讨论】:
这不会处理由其他对象组成的对象。它也不输出属性的值。只有名字。我已经知道了:P @Svish 但你的问题根本没有给出这个想法。请编辑。 @nawfal 其他人似乎理解得很好。 类型 t = typeof(T); foreach (var p in t.GetProperties()) System.Console.WriteLine(p.Name + " " + p.GetType().ToString()); 【参考方案3】:众所周知,ObjectDumper
类可以做到这一点。我从未确认过,但我一直怀疑即时窗口使用它。
编辑:我刚刚意识到,ObjectDumper
的代码实际上在您的机器上。前往:
C:/Program Files/Microsoft Visual Studio 9.0/Samples/1033/CSharpSamples.zip
这将解压缩到一个名为 LinqSamples 的文件夹。在那里,有一个名为 ObjectDumper 的项目。使用它。
【讨论】:
哇,这完全有效。尽管有一些深度控制会很好,哈哈。感谢您的精彩提示! =) 查看我的编辑。样本中的那个实际上有一个需要深度的重载。 嗯,是我一个人,还是把所有的东西都输出在一行上? 这可能很明显,但 VS2010 用户(很可能)会在此处找到它:C:\Program Files (x86)\Microsoft Visual Studio 10.0\Samples\1033 nuget package for ObjectDumper 现在可用。它还提供了DumpToString
和Dump
到Object
类的扩展方法。方便。【参考方案4】:
基于 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);
编辑
双向参考现已停止。因此,对象的 HashCode 存储在列表中。 AlreadyTouched 已修复(参见 cmets) FormatValue 已修复(参见 cmets)【讨论】:
小心这一点,如果你有双向对象引用,你可能会遇到 *** 异常 为什么要使用哈希?引用完整性还不够吗? 可能想要设置一个最大级别(可能不希望深入 10 个对象),如果元素是流,这将引发异常 如果对象为空,AlreadyTouched
会引发异常。您需要将if (value == null) return false;
添加到此方法的开头。
我对您的代码进行了一些更改并将其放在github.com/mcshaz/BlowTrial/blob/master/GenericToDataFile/… 上 - 这可以处理更复杂的对象,包括递归深度,跳过索引器和不带 getter 的属性,并避免对 StringBuiler ToString 的不必要调用。它还将“双向引用”消息更改为“引用已转储”,因为在哈希表中找到的引用并不总是双向的(例如重复相同实例的列表)。【参考方案5】:
不这么认为。我总是不得不编写它们或使用其他人的工作来获取这些信息。据我所知,必须是反思。
编辑:
检查this out。我正在调查对长对象图的一些调试,并在我添加手表时注意到了这一点,VS 在这个类中抛出:Mscorlib_CollectionDebugView<>
。它是一种内部类型,可以很好地显示集合,以便在监视窗口/代码调试模式下查看。现在因为它是内部的,您可以引用它,但您可以使用 Reflector 复制(来自 mscorlib)代码并拥有自己的代码(上面的链接有一个复制/粘贴示例)。看起来真的很有用。
【讨论】:
【参考方案6】:关于 Sean 的回复中的 TypeDescriptor(我无法发表评论,因为我的名声不好)...使用 TypeDescriptor 而不是 GetProperties() 的一个优点是 TypeDescriptor 具有在运行时动态将属性附加到对象和正常反射的机制会错过这些。
例如,当使用 PowerShell 的 PSObject(可以在运行时添加属性和方法)时,他们实现了一个自定义 TypeDescriptor,它将这些成员与标准成员集合并。通过使用 TypeDescriptor,您的代码无需了解这一事实。
组件、控件,我想也许 DataSets 也使用这个 API。
【讨论】:
【参考方案7】:任何其他解决方案/库最终都会使用反射来内省类型...
【讨论】:
当然可以,但如果不需要的话,我还是不想自己编写代码;)【参考方案8】:这正是反射的用途。我认为没有更简单的解决方案,但无论如何反射并不是代码密集型的。
【讨论】:
【参考方案9】:也许通过javascriptSerializer.Serialize?
【讨论】:
有趣...你会怎么用它? 你需要整理 JSON blob 以便它像样的......我会说它需要与编写你自己的反射代码一样多的行。但那是我的 2c。 好点cottsak。现在想通了如何使用它,尽管所有数据似乎都在那里,但开箱即用并不是很可读=) 这很好用;我使用JSON formatter 使其可读。 这是最好的答案。使用经过验证的框架,而不是推出自己的代码。以上是关于C#:打印对象的所有属性[重复]的主要内容,如果未能解决你的问题,请参考以下文章