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 现在可用。它还提供了DumpToStringDumpObject 类的扩展方法。方便。【参考方案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&lt;&gt;。它是一种内部类型,可以很好地显示集合,以便在监视窗口/代码调试模式下查看。现在因为它是内部的,您可以引用它,但您可以使用 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#:打印对象的所有属性[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何在C#中打印类的道具名称[重复]

打印对象的属性,如 json.dumps 打印一个字典 [重复]

如何在c#中检查对象是不是没有空属性[重复]

C#返回具有匹配属性的对象列表[重复]

C#使用列表属性展平对象列表[重复]

按字符串属性C#对对象列表进行排序[重复]