web-dev-qa-db-fra.com

Obtenir récursivement les propriétés et les propriétés enfant d'une classe

Je faisais quelque chose comme Obtenir récursivement les propriétés et les propriétés enfants d'un objet , mais je voulais utiliser la réflexion de manière récursive pour obtenir chaque propriété. Et j'ai eu le code de Imprimer récursivement les propriétés

Le problème avec le code est: il ne descend que d'un niveau, je me demande comment obtenir automatiquement toutes les propriétés en utilisant la réflexion? Je viens de créer l'exemple de code conteneur suivant:

public class Container
{
    public Bottle MyBottle { get; set; }
    public List<Address> Addresses { get; set; }

    public Container()
    {
        Address a = new Address();
        a.AddressLine1 = "1 Main St";
        a.AddressLine2 = "2 Main St";
        Addresses = new List<Address>();
        Addresses.Add(a);

        MyBottle = new Bottle();
        MyBottle.BottleName = "Big bottle";
        MyBottle.BottageAge = 2;
    }
}

public class Bottle
{
    public string BottleName { get; set; }
    public int BottageAge { get; set; }
}

public class Address
{
    public string AddressLine1 { get; set; }
    public string AddressLine2 { get; set; }
    public List<SpecialFolder> SpecialFolders { get; set; }

    public Address()
    {
        SpecialFolders = new List<SpecialFolder>();
        SpecialFolder sf = new SpecialFolder();
        sf.TemplateFolder = Environment.SpecialFolder.Templates.ToString();
        sf.UserFolder = Environment.SpecialFolder.UserProfile.ToString();
        SpecialFolders.Add(sf);
    }
}

public class SpecialFolder
{
    public string TemplateFolder { get; set; }
    public string UserFolder { get; set; }
}

Dans la méthode principale:

static void Main(string[] args)
{
    Container c = new Container();
    PrintProperties(c);
}
public static void PrintProperties(object obj)
{
    PrintProperties(obj, 0);
}
public static void PrintProperties(object obj, int indent)
{

    if (obj == null) return;
    string indentString = new string(' ', indent);
    Type objType = obj.GetType();
    PropertyInfo[] properties = objType.GetProperties();
    foreach (PropertyInfo property in properties)
    {
        object propValue = property.GetValue(obj, null);
        if (property.PropertyType.Assembly == objType.Assembly)
        {
            Console.WriteLine("{0}{1}:", indentString, property.Name);

            PrintProperties(propValue, indent + 2);
        }
        else
        {
            Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
        }
    }
}

J'espère avoir:

MyBottle:
      BottleName: Big bottle
      BottageAge: 2
Addresses:
      AddressLine1: 1 Main St
      AddressLine2: 2 Main St
      SpecialFolders:
            TemplateFolder: Templates
            UserFolder: UserProfile

Le résultat que j'obtiens maintenant:

MyBottle:
  BottleName: Big bottle
  BottageAge: 2
Addresses: System.Collections.Generic.List`1[TreeViewReflectionExample.Address]

Quelqu'un peut-il m'aider avec la méthode PrintProperties? Merci beaucoup.

26
HoKy22

Vous avez deux problèmes avec votre code:

  1. à cause de la condition if (property.PropertyType.Assembly == objType.Assembly) vous omettez System.Collections comme List<>
  2. vous ne traitez pas différemment propValue qui sont des collections. Par conséquent, il imprimera les propriétés List et non ses propriétés d'éléments. 

Vous pouvez changer cela par exemple en:

public void PrintProperties(object obj, int indent)
{    
    if (obj == null) return;
    string indentString = new string(' ', indent);
    Type objType = obj.GetType();
    PropertyInfo[] properties = objType.GetProperties();
    foreach (PropertyInfo property in properties)
    {
        object propValue = property.GetValue(obj, null);
        var elems = propValue as IList;
        if (elems != null)
        {
            foreach (var item in elems)
            {
                PrintProperties(item, indent + 3);
            }
        }
        else
        {
            // This will not cut-off System.Collections because of the first check
            if (property.PropertyType.Assembly == objType.Assembly)
            {
                Console.WriteLine("{0}{1}:", indentString, property.Name);

                PrintProperties(propValue, indent + 2);
            }
            else
            {
                Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
            }
        }
    }
}
44
Konrad Kokosa

Vous souhaitez gérer les types primitifs et les chaînes séparément, et effectuer une boucle sur les énumérables au lieu de simplement prendre leur valeur ToString (). Donc, votre code pourrait être mis à jour pour:

public void PrintProperties(object obj, int indent)
{

    if (obj == null) return;
    string indentString = new string(' ', indent);
    Type objType = obj.GetType();
    PropertyInfo[] properties = objType.GetProperties();
    foreach (PropertyInfo property in properties)
    {   
        object propValue = property.GetValue(obj, null);
        if(property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
            Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
        else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
        {
            Console.WriteLine("{0}{1}:", indentString, property.Name);
            IEnumerable enumerable = (IEnumerable)propValue;
            foreach(object child in enumerable)
                PrintProperties(child, indent + 2);
        }
        else 
        {
            Console.WriteLine("{0}{1}:", indentString, property.Name);
            PrintProperties(propValue, indent + 2);
        }
    }
}
10
driis

Cela fonctionne dans tous les cas sauf propValue est string []. Vous obtiendrez l'exception "incompatibilité de comptage de paramètres" en ligne: objet propValue = property.GetValue (obj, null);

Pour résoudre ce problème, vous pouvez utiliser ce code avec un petit correctif:

private void PrintProperties(object obj, int indent)
    {
        if (obj == null) return;
        string indentString = new string(' ', indent);
        Type objType = obj.GetType();
        PropertyInfo[] properties = objType.GetProperties();
        foreach (PropertyInfo property in properties)
        {
            object propValue = property.GetValue(obj, null);

            var elems = propValue as IList;
            if ((elems != null) && !(elems is string[]) )
            {
                foreach (var item in elems)
                {
                    PrintProperties(item, indent + 3);
                }
            }
            else
            {
                // This will not cut-off System.Collections because of the first check
                if (property.PropertyType.Assembly == objType.Assembly)
                {
                    LogToWindow(String.Format("{0}{1}:", indentString, property.Name));
                    PrintProperties(propValue, indent + 2);
                }
                else
                {
                    if (propValue is string[])
                    {
                        var str = new StringBuilder();
                        foreach (string item in (string[])propValue)
                        {
                            str.AppendFormat("{0}; ", item);
                        }
                        propValue = str.ToString();
                        str.Clear();
                    }
                    LogToWindow(String.Format("{0}{1}: {2}", indentString, property.Name, propValue));
                }
            }
        }
    }
1
Alexey Chaplanov