c#:打印对象的所有属性[复制]

时间:2022-04-06 21:32:56

This question already has an answer here:

这个问题已经有了答案:

Is there a method built in to .NET that can write all the properties and such of an object to the console? Could make one using reflection of course, but I'm curious to if this already exists... especially since you can do it in Visual Studio in the Immediate Window. There you can an object name (while in debug mode), press enter, and it is printed fairly prettily with all its stuff.

是否有一个内置到。net的方法,可以将所有的属性和对象写入控制台?当然可以用反射来表达,但我很好奇,如果这个已经存在……尤其是你可以在直接窗口的Visual Studio中完成。在这里您可以使用对象名称(在调试模式中),按enter,并且它的所有内容都非常漂亮。

Does a method like this exist?

这样的方法是否存在?

9 个解决方案

#1


55  

The ObjectDumper class has been known to do that. I've never confirmed, but I've always suspected that the immediate window uses that.

我们已经知道ObjectDumper类可以做到这一点。我从来没有确认过,但我一直怀疑,直接窗口会使用它。

EDIT: I just realized, that the code for ObjectDumper is actually on your machine. Go to:

编辑:我刚刚意识到,ObjectDumper的代码实际上在您的机器上。至:

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

c:/程序文件/微软Visual Studio 9.0/Samples/1033/CSharpSamples.zip。

This will unzip to a folder called LinqSamples. In there, there's a project called ObjectDumper. Use that.

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

( This will also make David in the comments happy :) )

(这也会让大卫在评论中开心:))

#2


240  

You can use the TypeDescriptor class to do this:

您可以使用TypeDescriptor类来执行以下操作:

foreach(PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj))
{
    string name=descriptor.Name;
    object value=descriptor.GetValue(obj);
    Console.WriteLine("{0}={1}",name,value);
}

TypeDescriptor lives in the System.ComponentModel namespace and is the API that Visual Studio uses to display your object in its property browser. It's ultimately based on reflection (as any solution would be), but it provides a pretty good level of abstraction from the reflection API.

类型描述符生活在系统中。ComponentModel名称空间,是Visual Studio用于在其属性浏览器中显示对象的API。它最终基于反射(正如任何解决方案一样),但是它提供了从反射API的相当好的抽象级别。

#3


78  

Based on the ObjectDumper of the LINQ samples I created a version that dumps each of the properties on its own line.

基于LINQ示例的ObjectDumper,我创建了一个版本,它将每个属性都转储到它自己的行中。

This Class Sample

这个类样本

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

has an output of

有一个输出

{MyNamespace.User}
  FirstName: "Arnold"
  LastName: "Schwarzenegger"
  Address: { }
    {MyNamespace.Address}
      Street: "6834 Hollywood Blvd"
      ZipCode: 90028
      City: "Hollywood"
  Hobbies: ...
    {MyNamespace.Hobby}
      Name: "body building"

Here is the code.

这是代码。

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 ("{ }");
    }
}

and you can use it like that:

你可以这样使用:

var dump = ObjectDumper.Dump(user);

Edit

编辑

  • Bi - directional references are now stopped. Therefore the HashCode of an object is stored in a list.
  • 双向引用现在停止了。因此,对象的HashCode存储在列表中。
  • AlreadyTouched fixed (see comments)
  • AlreadyTouched固定(见注释)
  • FormatValue fixed (see comments)
  • FormatValue固定(见注释)

#4


19  

Maybe via JavaScriptSerializer.Serialize?

也许通过JavaScriptSerializer.Serialize ?

#5


6  

Regarding TypeDescriptor from Sean's reply (I can't comment because I have a bad reputation)... one advantage to using TypeDescriptor over GetProperties() is that TypeDescriptor has a mechanism for dynamically attaching properties to objects at runtime and normal reflection will miss these.

关于Sean回复的TypeDescriptor(我不能评论,因为我的名声不好)……在GetProperties()上使用TypeDescriptor的一个优点是,TypeDescriptor有一个机制,可以在运行时动态地将属性附加到对象,而正常的反射将会忽略这些。

For example, when working with PowerShell's PSObject, which can have properties and methods added at runtime, they implemented a custom TypeDescriptor which merges these members in with the standard member set. By using TypeDescriptor, your code doesn't need to be aware of that fact.

例如,当使用PowerShell的大客户端时,它可以拥有在运行时添加的属性和方法,它们实现了一个自定义的TypeDescriptor,它将这些成员与标准的成员集合合并在一起。

Components, controls, and I think maybe DataSets also make use of this API.

组件,控件,我想也许数据集也会使用这个API。

#6


5  

Following snippet will do the desired 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);
}

I think if you write this as extension method you could use it on all type of objects.

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

#7


1  

This is exactly what reflection is for. I don't think there's a simpler solution, but reflection isn't that code intensive anyway.

这正是反思的意义所在。我不认为有一个简单的解决方案,但是反射并不是代码密集型的。

#8


0  

Any other solution/library is in the end going to use reflection to introspect the type...

任何其他的解决方案/库都将在最后使用反射来反省类型……

#9


0  

Don't think so. I've always had to write them or use someone else's work to get that info. Has to be reflection as far as i'm aware.

不这么认为。我总是不得不写它们或者用别人的工作来获得这些信息。就我所知,必须是反射。

EDIT:
Check this out. I was investigating some debugging on long object graphs and noticed this when i Add Watches, VS throws in this class: Mscorlib_CollectionDebugView<>. It's an internal type for displaying collections nicely for viewing in the watch windows/code debug modes. Now coz it's internal you can reference it, but u can use Reflector to copy (from mscorlib) the code and have your own (the link above has a copy/paste example). Looks really useful.

编辑:看看这个。我正在调查一些长对象图上的调试,并注意到当我添加手表时,VS在这个类中抛出:Mscorlib_CollectionDebugView<>。它是一种内部类型,用于在表窗口/代码调试模式中很好地显示集合。现在,因为它是内部的,您可以引用它,但是您可以使用反射器复制(从mscorlib)代码并拥有您自己的(上面的链接有一个复制/粘贴示例)。看起来很有用。

#1


55  

The ObjectDumper class has been known to do that. I've never confirmed, but I've always suspected that the immediate window uses that.

我们已经知道ObjectDumper类可以做到这一点。我从来没有确认过,但我一直怀疑,直接窗口会使用它。

EDIT: I just realized, that the code for ObjectDumper is actually on your machine. Go to:

编辑:我刚刚意识到,ObjectDumper的代码实际上在您的机器上。至:

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

c:/程序文件/微软Visual Studio 9.0/Samples/1033/CSharpSamples.zip。

This will unzip to a folder called LinqSamples. In there, there's a project called ObjectDumper. Use that.

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

( This will also make David in the comments happy :) )

(这也会让大卫在评论中开心:))

#2


240  

You can use the TypeDescriptor class to do this:

您可以使用TypeDescriptor类来执行以下操作:

foreach(PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj))
{
    string name=descriptor.Name;
    object value=descriptor.GetValue(obj);
    Console.WriteLine("{0}={1}",name,value);
}

TypeDescriptor lives in the System.ComponentModel namespace and is the API that Visual Studio uses to display your object in its property browser. It's ultimately based on reflection (as any solution would be), but it provides a pretty good level of abstraction from the reflection API.

类型描述符生活在系统中。ComponentModel名称空间,是Visual Studio用于在其属性浏览器中显示对象的API。它最终基于反射(正如任何解决方案一样),但是它提供了从反射API的相当好的抽象级别。

#3


78  

Based on the ObjectDumper of the LINQ samples I created a version that dumps each of the properties on its own line.

基于LINQ示例的ObjectDumper,我创建了一个版本,它将每个属性都转储到它自己的行中。

This Class Sample

这个类样本

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

has an output of

有一个输出

{MyNamespace.User}
  FirstName: "Arnold"
  LastName: "Schwarzenegger"
  Address: { }
    {MyNamespace.Address}
      Street: "6834 Hollywood Blvd"
      ZipCode: 90028
      City: "Hollywood"
  Hobbies: ...
    {MyNamespace.Hobby}
      Name: "body building"

Here is the code.

这是代码。

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 ("{ }");
    }
}

and you can use it like that:

你可以这样使用:

var dump = ObjectDumper.Dump(user);

Edit

编辑

  • Bi - directional references are now stopped. Therefore the HashCode of an object is stored in a list.
  • 双向引用现在停止了。因此,对象的HashCode存储在列表中。
  • AlreadyTouched fixed (see comments)
  • AlreadyTouched固定(见注释)
  • FormatValue fixed (see comments)
  • FormatValue固定(见注释)

#4


19  

Maybe via JavaScriptSerializer.Serialize?

也许通过JavaScriptSerializer.Serialize ?

#5


6  

Regarding TypeDescriptor from Sean's reply (I can't comment because I have a bad reputation)... one advantage to using TypeDescriptor over GetProperties() is that TypeDescriptor has a mechanism for dynamically attaching properties to objects at runtime and normal reflection will miss these.

关于Sean回复的TypeDescriptor(我不能评论,因为我的名声不好)……在GetProperties()上使用TypeDescriptor的一个优点是,TypeDescriptor有一个机制,可以在运行时动态地将属性附加到对象,而正常的反射将会忽略这些。

For example, when working with PowerShell's PSObject, which can have properties and methods added at runtime, they implemented a custom TypeDescriptor which merges these members in with the standard member set. By using TypeDescriptor, your code doesn't need to be aware of that fact.

例如,当使用PowerShell的大客户端时,它可以拥有在运行时添加的属性和方法,它们实现了一个自定义的TypeDescriptor,它将这些成员与标准的成员集合合并在一起。

Components, controls, and I think maybe DataSets also make use of this API.

组件,控件,我想也许数据集也会使用这个API。

#6


5  

Following snippet will do the desired 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);
}

I think if you write this as extension method you could use it on all type of objects.

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

#7


1  

This is exactly what reflection is for. I don't think there's a simpler solution, but reflection isn't that code intensive anyway.

这正是反思的意义所在。我不认为有一个简单的解决方案,但是反射并不是代码密集型的。

#8


0  

Any other solution/library is in the end going to use reflection to introspect the type...

任何其他的解决方案/库都将在最后使用反射来反省类型……

#9


0  

Don't think so. I've always had to write them or use someone else's work to get that info. Has to be reflection as far as i'm aware.

不这么认为。我总是不得不写它们或者用别人的工作来获得这些信息。就我所知,必须是反射。

EDIT:
Check this out. I was investigating some debugging on long object graphs and noticed this when i Add Watches, VS throws in this class: Mscorlib_CollectionDebugView<>. It's an internal type for displaying collections nicely for viewing in the watch windows/code debug modes. Now coz it's internal you can reference it, but u can use Reflector to copy (from mscorlib) the code and have your own (the link above has a copy/paste example). Looks really useful.

编辑:看看这个。我正在调查一些长对象图上的调试,并注意到当我添加手表时,VS在这个类中抛出:Mscorlib_CollectionDebugView<>。它是一种内部类型,用于在表窗口/代码调试模式中很好地显示集合。现在,因为它是内部的,您可以引用它,但是您可以使用反射器复制(从mscorlib)代码并拥有您自己的(上面的链接有一个复制/粘贴示例)。看起来很有用。