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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/852181/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 04:12:36  来源:igfitidea点击:

C#: Printing all properties of an object

c#genericsconsoleobject

提问by Svish

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 中的立即窗口中完成它。在那里你可以输入一个对象名称(在调试模式下),按回车键,它和它的所有东西都被漂亮地打印出来。

Does a method like this exist?

有这样的方法吗?

采纳答案by BFree

The ObjectDumperclass 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 ObjectDumperis actually on your machine. Go to:

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

C:/Program Files/Microsoft 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 :) )

(这也会让评论中的大卫高兴:))

回答by Matt Kocaj

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<>。它是一种内部类型,用于在监视窗口/代码调试模式下很好地显示集合。现在因为它是内部的,您可以引用它,但是您可以使用 Reflector 复制(从 mscorlib)代码并拥有自己的代码(上面的链接有一个复制/粘贴示例)。看起来真的很有用。

回答by Marc Gravell

回答by Jon B

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

这正是反射的用途。我认为没有更简单的解决方案,但无论如何反射并不是代码密集型的。

回答by TheVillageIdiot

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.

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

回答by mP.

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

任何其他解决方案/库最终都会使用反射来内省类型......

回答by Sean

You can use the TypeDescriptorclass 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.ComponentModelnamespace 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.

TypeDescriptor 位于System.ComponentModel命名空间中,是 Visual Studio 用于在其属性浏览器中显示对象的 API。它最终基于反射(就像任何解决方案一样),但它从反射 API 提供了相当好的抽象级别。

回答by Josh

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(我无法发表评论,因为我的名声不好)......使用 TypeDescriptor 而不是 GetProperties() 的一个优势是 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 的 PSObject(可以在运行时添加属性和方法)时,他们实现了一个自定义 TypeDescriptor,将这些成员与标准成员集合并。通过使用 TypeDescriptor,您的代码不需要知道这个事实。

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

组件、控件,我想也许 DataSet 也使用了这个 API。

回答by ms007

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 == '
var dump = ObjectDumper.Dump(user);
') return string.Empty; if (o is ValueType) return (o.ToString()); if (o is IEnumerable) return ("..."); return ("{ }"); } }

and you can use it like that:

你可以这样使用它:

##代码##

Edit

编辑

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