C#: impression de toutes les propriétés d'un objet [dupliquer]

cette question a déjà une réponse ici:

y a-t-il une méthode intégrée à .NET qui puisse écrire toutes les propriétés et telles d'un objet à la console? Pourrait en faire un en utilisant la réflexion bien sûr, mais je suis curieux de savoir si cela existe déjà... surtout depuis que vous pouvez le faire dans Visual Studio dans la fenêtre immédiate. Là vous pouvez choisir un nom d'objet (en mode debug), appuyez sur Entrée, et il est imprimé assez joliment avec tous ses trucs.

est-ce qu'une telle méthode existe?

143
demandé sur Svish 2009-05-12 14:53:04

9 réponses

la classe ObjectDumper est connue pour faire cela. Je n'ai jamais confirmé, mais j'ai toujours suspecté que la fenêtre immédiate utilise ça.

EDIT: je viens de réaliser, que le code pour ObjectDumper est en fait sur votre machine. Aller à:

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

ceci décompressera vers un dossier appelé LinqSamples. Là-dedans, il y a un projet appelé ObjectDumper. Utiliser que.

( Cela permettra également de faire de David dans les commentaires: heureux:) )

56
répondu BFree 2009-05-12 11:16:08

vous pouvez utiliser la classe TypeDescriptor pour ce faire:

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

Ecrit dactylographié vit dans le système .ComponentModel namespace et est L'API que Visual Studio utilise pour afficher votre objet dans son navigateur de propriétés. Il est finalement basé sur la réflexion (comme toute solution), mais il fournit un assez bon niveau d'abstraction à partir de l'API de réflexion.

259
répondu Sean 2009-05-12 11:59:38

basé sur L'ObjectDumper des échantillons LINQ j'ai créé une version qui décharge chacune des propriétés sur sa propre ligne.

Échantillon De Cette Classe

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

a une sortie de

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

voici le 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 == '"151920920"') 
            return string.Empty; 

        if (o is ValueType)
            return (o.ToString());

        if (o is IEnumerable)
            return ("...");

        return ("{ }");
    }
}

et vous pouvez l'utiliser comme ça:

var dump = ObjectDumper.Dump(user);

Modifier

  • Bi - les références directionnelles sont maintenant arrêtées. Par conséquent, le HashCode d'un objet est stocké dans une liste.
  • AlreadyTouched fixe (voir commentaires)
  • FormatValue fixe (voir les commentaires)
82
répondu ms007 2014-04-14 09:22:09
19
répondu Marc Gravell 2009-05-12 10:59:05

concernant le manuscrit dactylographié de la réponse de Sean (Je ne peux pas commenter parce que j'ai une mauvaise réputation)... un avantage à utiliser TypeDescriptor par rapport à GetProperties() est que TypeDescriptor a un mécanisme pour attacher dynamiquement les propriétés aux objets à l'exécution et la réflexion normale les manquera.

par exemple, en travaillant avec le PSObject de PowerShell, qui peut avoir des propriétés et des méthodes ajoutées à l'exécution, ils ont implémenté un script personnalisé qui fusionne ces membres dans avec la norme de l'ensemble des membres. En utilisant TypeDescriptor, votre code n'a pas besoin d'être conscient de ce fait.

Composants, contrôles, et je pense que peut-être DataSets également l'utilisation de cette API.

6
répondu Josh 2009-05-12 13:58:31

l'extrait suivant fera la fonction désirée:

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

je pense que si vous écrivez ceci comme méthode d'extension vous pourriez l'utiliser sur tous les types d'objets.

5
répondu TheVillageIdiot 2009-05-12 11:12:33

c'est exactement à cela que sert la réflexion. Je ne pense pas qu'il y ait une solution plus simple, mais la réflexion n'est pas ce code intensif de toute façon.

1
répondu Jon B 2009-05-12 11:09:46

toute autre solution/Bibliothèque est en fin de Compte va utiliser la réflexion pour introspecter le type...

0
répondu mP. 2009-05-12 11:12:53

Je ne pense pas. J'ai toujours dû les écrire ou utiliser le travail de quelqu'un d'autre pour obtenir cette information. Ça doit être de la réflexion autant que je sache.

EDIT:

Regardez et . J'étudiais quelques débogage sur des graphiques d'objets longs et j'ai remarqué cela quand J'ajoute des montres, VS jette dans cette classe: Mscorlib_CollectionDebugView<> . C'est un type interne pour l'affichage des collections bien pour l'affichage de la montre modes de débogage windows/code. Maintenant que C'est interne, vous pouvez le référencer, mais vous pouvez utiliser Reflector pour copier (à partir de mscorlib) le code et avoir le vôtre (le lien ci-dessus a un exemple de copier/coller). On dirait vraiment utile.

0
répondu Matt Kocaj 2009-05-13 06:40:13