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

Svi*_*ish 172 c# generics console object

是否有一个内置于.NET的方法可以将所有属性和对象写入控制台?当然可以使用反射制作一个,但我很好奇,如果这已经存在...特别是因为你可以在立即窗口中的Visual Studio中完成它.在那里你可以有一个对象名称(在调试模式下),按回车键,并且它的所有内容都打印得非常漂亮.

这样的方法存在吗?

Sea*_*ean 300

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

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

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

  • 在你的答案中提到命名空间的好工作! (7认同)
  • @Best_Where_Gives-因此,您可以扩展代码以处理此问题,只要引擎已完成即可。有时您必须自己编写一些代码。 (2认同)

ms0*_*007 95

基于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; }    
    }
}
Run Code Online (Sandbox Code Playgroud)

有一个输出

{MyNamespace.User}
  FirstName: "Arnold"
  LastName: "Schwarzenegger"
  Address: { }
    {MyNamespace.Address}
      Street: "6834 Hollywood Blvd"
      ZipCode: 90028
      City: "Hollywood"
  Hobbies: ...
    {MyNamespace.Hobby}
      Name: "body building"
Run Code Online (Sandbox Code Playgroud)

这是代码.

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 ("{ }");
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它:

var dump = ObjectDumper.Dump(user);
Run Code Online (Sandbox Code Playgroud)

编辑

  • 双向引用现已停止.因此,对象的HashCode存储在列表中.
  • AlreadyTouched已修复(见评论)
  • FormatValue已修复(请参阅注释)

  • 我使用了你的代码进行了一些更改并将其放在https://github.com/mcshaz/BlowTrial/blob/master/GenericToDataFile/ObjectDumper.cs上 - 它处理更复杂的对象,包括递归深度,跳过索引器和没有getter的属性,并避免对StringBuiler ToString进行不必要的调用.它还将'双向引用'消息更改为'reference already dumped',因为在散列表中找到的引用并不总是双向的(例如,重复相同实例的列表). (19认同)
  • 如果对象为null,则"AlreadyTouched"会抛出异常.您将要在此方法的开头添加`if(value == null)return false;`. (3认同)

BFr*_*ree 64

已知ObjectDumper类可以做到这一点.我从来没有确认过,但我一直怀疑即时窗口会使用它.

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

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

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

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

  • ObjectStumper的[nuget package](http://www.nuget.org/packages/ObjectDumper/)现已推出.它还提供了一个扩展方法`DumpToString`和`Dump`到`Object`类.便利. (7认同)
  • 这可能是显而易见的,但VS2010用户将(很可能)在这里找到它:C:\ Program Files(x86)\ Microsoft Visual Studio 10.0\Samples\1033 (6认同)
  • 可以在没有明显许可的情况下深入链接到某个项目.双击倾销成员时使用的"catch {}".仍然+1为一个很好的例子来说明它是如何完成的. (3认同)

Mar*_*ell 25

也许通过JavaScriptSerializer.Serialize

  • 有趣...你会如何使用它? (2认同)
  • 这很好用; 我使用[JSON格式化程序](http://jsonformatter.curiousconcept.com/)使其可读. (2认同)
  • 我喜欢这种方法。它很轻,输出也很清晰。就我而言,我使用 [Newtonsoft 的 JSON.net](https://www.newtonsoft.com/json),如下所示, `var respJson = JsonConvert.SerializeObject(objResponse, Formatting.Indented); Console.WriteLine(respJson); ` (2认同)

Jos*_*osh 6

关于Sean的回复中的TypeDescriptor(我不能发表评论,因为我名声不好)...使用TypeDescriptor优于GetProperties()的一个优点是TypeDescriptor有一个机制,可以在运行时动态地将属性附加到对象,而普通反射将会遗漏这些.

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

组件,控件,我认为DataSet也可以使用这个API.


The*_*iot 5

以下代码片段将执行所需的功能:

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());
}
Run Code Online (Sandbox Code Playgroud)

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

  • 但这不能处理由其他对象组成的对象。它也不输出属性的值。只有名字。我已经知道这些了:P (3认同)
  • @Svish,但你的问题根本没有给出这个想法。请编辑。 (2认同)
  • @nawfal其他人似乎很好。 (2认同)