Faire la liste arborescente des membres d'une classe

Résolu
MGD Software Messages postés 186 Date d'inscription vendredi 1 septembre 2006 Statut Membre Dernière intervention 23 avril 2022 - Modifié le 21 nov. 2017 à 20:03
MGD Software Messages postés 186 Date d'inscription vendredi 1 septembre 2006 Statut Membre Dernière intervention 23 avril 2022 - 22 nov. 2017 à 11:08
Bonjour,

J'ai un problème qui me tient depuis plusieurs jours, et tout ce j'ai pu trouver sur le web ne répond pas à mon problème.

J'ai une classe très complexe destinée à la lecture/écriture d'un fichier XML.
Cette classe comprend des sous-classes, qui elles-même contiennent des sous-classes, etc., chaque niveau comportant des variables en plus des sous-classes. J'ai actuellement 4 niveaux, mais si on dépasse 2, je pense que l’algorithmie est trouvée.

Comme c'est complexe à appréhender lors de la programmation (surtout quand on déclare une instance du quatrième niveau), et que ça évolue en permanence au fur et à mesure de l'élaboration du programme, je souhaite établir à des fins de documentation une routine qui me fournirait la liste des variables, des classes et de leurs membres.

Le code faisant plus de 300 lignes, je vais juste présenter un exemple qui j'espère reflètera la réalité :
classe_n1
 constructeur()
 IN21 = instance1 de classe_n2_1
 IN22 = instance2 de classe_n2_2
 variable 1 {get; set;}
 variable 2 {get; set;}
 
 classe_n2_1
  constructeur();
  IN3 = instance de classe n3
  variable1 {get; set;}
  variable2 {get; set;}
  
  classe_n3
   constructeur()
   variable1 {get; set;}
   variable2 {get { return "Ok"; }
   variable3 {get; set;}
 
 classe_n2_2
  constructeur()
etc...  
 
Je précise que tout est déclaré public.

L'avantage de ce principe est qu'il est facile d'accéder aux variables de n'importe que niveau en ne déclarant qu'une instance de la classe de niveau 1 :
new classe_n1().IN21.IN3.Variable2
renvoie "OK".
Et avec serialize(), on obtient un fichier XML parfaitement conforme à la structure de la classe avec une seule ligne de code.

J'ai tenté d'utiliser la fonction GetProperties(), mais elle ne me ramène que les variables du premier niveau, et même pas les instances des classes de niveau 2 et inférieurs.

J'ai aussi essayé l'utilisation d'une sérialisation dans un flux mémoire, puis une relecture avec une XmlDocument et des SelectNodes, mais ça ne donne pas le résultat voulu. Notamment, cela ne ramène pas les membres des classes lorsque ce sont des listes vides.

Quelqu'un aurait une idée ou déjà réalisé ce genre de chose ?
Merci.

2 réponses

Whismeril Messages postés 19026 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 avril 2024 656
21 nov. 2017 à 21:47
Bonsoir,


pour la doc il y a déjà des outils qui existent.

Soient les classes suivantes:

   /// <summary>
    /// Classe définissant l'outil A
    /// </summary>
    /// <remarks>C'est juste un exemple</remarks>
    class ClassA
    {
        /// <summary>
        /// Valeur entière vachement importante
        /// </summary>
        public int Entier { get; set; }

        /// <summary>
        /// Un texte tout aussi important
        /// </summary>
        public string Texte { get; set; }

        /// <summary>
        /// Ca c'est une instance de ClassB
        /// </summary>
        public ClassB B { get; set; }

        /// <summary>
        /// Génère l'instance de ClassB
        /// </summary>
        /// <param name="Toto">Nombre pour générer B</param>
        /// <param name="Tutu">Texte pour générer B</param>
        /// <returns>B</returns>
        public ClassB GenereClassB(int Toto, string Tutu)
        {
            B = new ClassB { Toto = Toto, Tutu = Tutu, C = new ClassC() };
            return B;
        }
    }

    /// <summary>
    /// Là on définit l'outil B
    /// </summary>
    class ClassB
    {
        /// <summary>
        /// Un autre entier très utile
        /// </summary>
        public int Toto { get; set; }

        /// <summary>
        /// Pareil pour ce texte
        /// </summary>
        public string Tutu { get; set; }

        /// <summary>
        /// Là c'est une instance de ClassC
        /// </summary>
        public ClassC C { get; set; }
    }

    /// <summary>
    /// Définite l'outil C
    /// </summary>
    class ClassC
    {
        /// <summary>
        /// Encore un entier
        /// </summary>
        /// <value>3</value>
        public int EntierC { get; set; } = 3;
    }


Avec une version pro de VS (j'ai pas ça à la maison ;) ), tu peux faire un diagramme de classe.
Voir le schéma sur cette page
https://msdn.microsoft.com/fr-fr/library/dd409437(v=vs.120).aspx



En complément, à condition d'avoir bien commenté tes membres (avec les /// et les balises comme au dessus), tu peux utiliser Doxygen
https://fr.wikipedia.org/wiki/Doxygen
Ça fait une doc html, voilà le résultat pour mon exemple
http://www.cjoint.com/c/GKvuVlxAml5
0
MGD Software Messages postés 186 Date d'inscription vendredi 1 septembre 2006 Statut Membre Dernière intervention 23 avril 2022 2
Modifié le 22 nov. 2017 à 11:31
Merci pour la réponse.

Cependant, je n'ai pas la version pro, et en tant que retraité, je n'ai pas les moyens de me la payer, développant désormais des freewares. Je travaille avec la version Community 2015, gratuite (Merci Microsoft, pour une fois), qui est suffisante pour mes besoins actuels. Il manque un générateur de packages (ClickOnce ne me convient pas du tout), mais je les génère avec NSIS, comme je faisais avec VB6. C'est même beaucoup plus simple, car il n'y a plus à faire la chasse aux composants COM, il suffit d'installer le framework qui va bien.

D'autre part, une sortie HTML ne m'intéresse pas, car je veux pouvoir coller le résultat dans Excel et travailler dessus. En plus, j'ai déjà la sortie HTML, avec un Serialize tout bête. Je ne veux pas récupérer les commentaires (toutes mes fonctions sont cependant préfacées avec ///) ni les valeurs mais juste la liste des objets, sous une forme équivalente à un TreeView.

Je crois que je vais me contenter de ma routine (ci-dessous), qui est ce qui approche le mieux ce que je souhaite. En plus, cette routine prend en compte ce que je veux masquer, avec les attributs [XmlIgnore]

Merci pour le lien sur Docygen, mais ce j'en ai vu ne me convient pas : beaucoup trop verbeux à mon goût. Je préfère faire plus condensé, et je continuerai à faire "a la mano" sous Word, comme je fais depuis plus de 20 ans. On ne se refait pas à mon âge.

@+

public string ClassList(Object MyClass)
{
    XmlSerializer ser = new XmlSerializer(MyClass.GetType());
    using (MemoryStream memStream = new MemoryStream())
    {
        XmlWriter writer = XmlWriter.Create(memStream);
        ser.Serialize(writer, MyClass);
        memStream.Position = 0;
        XmlDocument xml = new XmlDocument();
        xml.Load(memStream);
        XmlNode Nd = xml.SelectSingleNode("/");
        string Result = "";
        PrintNode(ref Result, Nd, 0);
        return Result;
    }
}

private void PrintNode(ref string Result, XmlNode Nd, int Rank)
{
    if(Nd.Name != "#text")
        Result += new String('\t', Rank) + Nd.Name + "\n";
    foreach (XmlNode Nd1 in Nd.ChildNodes)
        PrintNode(ref Result, Nd1, Rank + 1);
}

à appeler avec
Console.Write(ClassList(MonInstanceDeClasse))

Attention, la classe DOIT être sérializable, sinon une erreur se produit au niveau du "new XmlSerializer(MyClass.GetType())"
0
Rejoignez-nous