Reflection et Instances d'attributs [Résolu]

Signaler
Messages postés
64
Date d'inscription
lundi 21 avril 2008
Statut
Membre
Dernière intervention
23 octobre 2009
-
Messages postés
1160
Date d'inscription
vendredi 23 juillet 2004
Statut
Membre
Dernière intervention
21 octobre 2010
-
Bonjour,

J'ai un objet List<T>.

Je veux que T soit en fonction d'un objet récupérer par réflexion. Je l'ai récupéré mais je sais pas comment l'écrire.

Si mon objet o est du type System.int32; comment faire pour écrire :

    List<o.getType()> lst ... ;
    List<typeof(o)> lst .... ;

Ces examples ne marchent pas :( Je continue à chercher mais si quelqu'un peut m'aider ... Merci à lui !
A+

14 réponses

Messages postés
1024
Date d'inscription
mardi 4 février 2003
Statut
Membre
Dernière intervention
7 juin 2010
63
A mon avis, tu es obligé de passer une nouvelle fois par la réflexion et par la méthode MakeGenericType.

Un peu plus de renseignement à ce sujet :
http://msdn.microsoft.com/fr-fr/library/b8ytshk6.aspx

Amicalement, SharpMao

"C'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!"
(Coluche / 1944-1986 / Pensées et anecdotes)
Messages postés
1160
Date d'inscription
vendredi 23 juillet 2004
Statut
Membre
Dernière intervention
21 octobre 2010
18
les templates sont résolus a la compilation, alors que les attributs et la reflection sont résolus au runtime. tu peux donc pas des la compilation demander au prog de faire un truc qui ne pourra etre résolu qu'au runtime.
En bref, l'instanciation comme tu le fais tente de créer un type qui doit etre determiné a la compilation, alors qu'il nest réellement connu qu'au runtime.
Bref, si tu commence par la reflection, tu devra continuer sur la reflection (et donc créer l'instance comme te l'a dis sharpmao).
Messages postés
64
Date d'inscription
lundi 21 avril 2008
Statut
Membre
Dernière intervention
23 octobre 2009

Information super précise et complète!
Merci à vous deux, je vais regarder le lien que vous m'avez donné ...
Messages postés
64
Date d'inscription
lundi 21 avril 2008
Statut
Membre
Dernière intervention
23 octobre 2009

MakeGenericType me renvoit un nouveau type et je reboucle sur mon problème.

A aucun moment, je ne peut créer une liste de type t où t a été généré par MakeGenericType.

Il doit manquer une étape pour pouvoir récupérer une instance de l'objet ?

    Object o = FieldInfo.GetValue(pObject); // A partir de mon FieldInfo, je récupère l'attribut de l'objet pObject.

Mais je ne veux pas un Object, je veux un type customisé, genre un objet Person, pour avoir une List, et pouvoir ensuite appeller des méthodes sur mes objets Person.

Vous auriez une explication complémentaire ?

Merci d'avance.
Messages postés
1024
Date d'inscription
mardi 4 février 2003
Statut
Membre
Dernière intervention
7 juin 2010
63
Oui, c'est pour ça que j'ai donné le 2ème lien http://msdn.microsoft.com/fr-fr/library/b8ytshk6.aspx
Il y a un exemple complet.

Amicalement, SharpMao

"C'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!"
(Coluche / 1944-1986 / Pensées et anecdotes)
Messages postés
64
Date d'inscription
lundi 21 avril 2008
Statut
Membre
Dernière intervention
23 octobre 2009

Effectivemment, je l'ai bien regardé.

Mais il y'a ce problème d'objet...

J'arrive correctement à récupérer un Object, et dans mes variables locales, je vois bien quelle est du type List ou List<String>.

Mais je n'arrive pas à l'utiliser pour le "caster" correctement et pouvoir profiter des méthodes de l'objet.

Type[] typeArgs = {typeof(string), typeof(Example)};

// Construct the type Dictionary<String, Example>.
Type constructed = d1.MakeGenericType(typeArgs);

DisplayGenericType(constructed);

object o = Activator.CreateInstance(constructed);

J'ai l'équivalent de cela, j'arrive à récupérer mon objet o qui est dans mon application un type ReflectionTest.Types.DbTypes.
Mais je n'arrive pas à dire que mon objet, je veux l'utiliser comme un DbTypes et pas comme un objet.

Désolé d'insister :)
Messages postés
1160
Date d'inscription
vendredi 23 juillet 2004
Statut
Membre
Dernière intervention
21 octobre 2010
18
ca depend ce que tu veux faire dessus, mais a priori tu dois le caster dans une des interfaces qu'implemente la classe List (IList, ICollection, etc...enfin celle qui t'arrange, regarde la doc de la classe list puis regarde la doc des différentes interfaces implémentéespar la classe et fais ton choix. a priori si tu veux faire un foreach ca sera ICollection, accéder a l'élément d'indice X, ca sera IList, etc...)
Messages postés
64
Date d'inscription
lundi 21 avril 2008
Statut
Membre
Dernière intervention
23 octobre 2009

Aie.
En fait quand je parle de la classe List, c'est simplement un exemple. Dans mon post précédent, j'explique que mon type est bien un DbType qui est une classe personnalisée. Donc pas d'interfaces :/

J'aurais réellement besoin de réussir à faire ça, et je pense que quand j'aurais réussi, je filerai mon code pour que cela aide un jour parce que je sèche complètement et mes recherches sont vaines.
Messages postés
1160
Date d'inscription
vendredi 23 juillet 2004
Statut
Membre
Dernière intervention
21 octobre 2010
18
dans ce cas tout dépend de ce que t uveux faire. si tu le peux (en fonction de ton besoin) tu devrais créer une interface non générique, implémentée par ton dbType. Dans ce cas tu pourras caster ton object dans le type de cette interface et appeler tes méthodes.
En revanche, si tu as besoin du type utilisé par la généricité (le ou <string>) tu devras continuer par reflection. Le principe reste le meme : tu ne peux pas determiner a la compilation un type généré au runtime!
apres il te reste la possibilité que ton design soit mal adapté au besoin (peut etre qu'un simple schéma d'héritage aurait suffit et été plus simple et fonctionnel si tu n'instancie ton objet que pour int et string?)

Bref, ici tout est question de ce que tu veux faire
Messages postés
64
Date d'inscription
lundi 21 avril 2008
Statut
Membre
Dernière intervention
23 octobre 2009

Ce que je veux faire est relativemment simple dans l'idée :

Je veux créer un Type définit (le DbType) qui est une classe générique.
Ensuite, j'ai des objets qui ont pour attributs des DbType (Par exemple un DbType identifiant et un DbType<string> nom).

En utilisant la réflexion sur cet objet, je récupère les différents FieldInfos associés aux attributs.
A partir de ce FieldInfo, je sais que le type de mon DbType est un int ou un string ou autre... (Je récupère la chaine de caratère "System.Int32" et "System.String") et  je peux récupérer l'instance de l'objet en utilisant getValue() (J'aurais pu passer par Activator.createInstance(), c'est pareil).

Mon problème est de caster cet objet en un DbType ou DbType<String> pour pouvoir utiliser ces propres methodes (Par exemple : la méthode GetValue du DbType renvoit un int alors que celle du DbType<string> Renvoie un string)

Pour mieux comprendre, voici monde code lors de la réflexion. J'éspère que mon problème sera plus clair et que ca vous donnera une idée plus précise :

public SQLGenerator(Persistant pObject)
        {
            fields = new List<SQLField>();
            Type pObjectType = pObject.GetType();

            // Récupération du nom de l'objet qui sera utilisé pour le nom de la table
            tableName = pObjectType.Name;

            // Récupération des attributs de l'objet
            BindingFlags bf = BindingFlags.Instance | BindingFlags.NonPublic;
            FieldInfo[] fieldsInfo = pObjectType.GetFields(bf);

            // Si l'objet a des attributs
            if (fieldsInfo.Length > 0)
            {
                // Création des SQLField en fonction des FieldInfos
                foreach (FieldInfo fInfo in fieldsInfo)
                {
                    // Récupération du Type DbType;
                    if ( fInfo.FieldType.Name.StartsWith("DbType"))
                    {
                        // Récupération de l'objet DbType
                        Object o = fInfo.GetValue(pObject); // Le debug m'indique que c'est un DbType

                        // Récupération du Type t dans DbType<t>
                        string fullname = fInfo.FieldType.FullName;
                        // TODO : Extraire proprement le type de l'objet générique !
                        int indexBegin = fullname.IndexOf("System.");
                        fullname = fullname.Substring(indexBegin + 7);
                        int indexEnd = fullname.IndexOf(',');                        string TTypeName fullname.Substring(0, indexEnd); // TTypeName String ou Int32 par exemple

                         // Ce que j'aimerai faire :
                     // Récupérer le DbType adéquat en fonction de mon FieldInfo
                     DbType< TTypeName> dbtype = (DbType<TTypeName>) o;
                         // Utiliser les méthodes et propriétés de mon DbType
                         String Value = DbType.value.toString();
                         Bool test = DbType.test;

                         // .... Suite de ma fonction
                       
                     }
                 }
               }

Comment dois-je faire (Pas de ça depend faut voir please :)) ?

Merci d'avance,
Messages postés
1160
Date d'inscription
vendredi 23 juillet 2004
Statut
Membre
Dernière intervention
21 octobre 2010
18
bien donc ton design n'est pas correct. d'un point de vue conceptuel, tu ne peux pas instancier ton générique par reflection (donc un typage "dynamique") pour ensuite le caster et en faire un usage statique (en gros tu vas faire : if (o is dbType){} else if (o is dbType<string>{}, etc). cest moche, cest porc, et ca enleve tout l'interet d'avoir de la reflection, du code dynamique, et des métadonnées.Donc, tu as plusieurs solutions :
1 - créer des attributs personnalisés pour les méthodes que tu dois appeler, afin de les appeler correctement par réflection
2 - appeler tes méthodes directement par réflection sans autres ajouts (non compatible avec le point 1, evidemment) mais dans ton cas je dirais que cest pas possible
3 - enlever l'utilisation de génériques créés par reflection (donc au runtime) et faire ca a la compilation (conception plus simple, voire meme sans templates du tout).

D'un point de vue conceptuel, je dirais qu'il te faut plutot utiliser le point 3. le fait est que tu as l'air de n'utiliser les templates que pour un nombre limité de types (2?). a partir de la, le coté reflexion est inutile, et peut etre le coté générique aussi. A la limite ce coté reflexion aurait pu te donner un code super propre et evolutif, le probleme est que le design nest pas cohérent avec ce que tu veux faire
Messages postés
64
Date d'inscription
lundi 21 avril 2008
Statut
Membre
Dernière intervention
23 octobre 2009

Une autre solution me permettant de créer une List de Type DbType<X> (X peut toujours prendre les valeurs Int, String et autres...) pourrait résoudre mon problème.

Le soucis est de comment déclarer le faire que X peut prendre plusieurs valeur ? Le fait de passer par une interface IDbType qui serait implémentée par DbType ne résoud pas le soucis. Il le reporte puisque à un moment il faudra que je fasse mon "Cast" en DbType<Z> ou Z est un type particulier.

       fields = new List();
       //et plus loin...
       fields.Add((IDbType) o);

Vous allez me dire que IDbType doit avoir le prototype de la propriété Value par exemple. Mais là aussi le prototype dépend du type de DbType.
Le serpent se mort la queue... J'ai vraiment besoin d'aide...
Messages postés
64
Date d'inscription
lundi 21 avril 2008
Statut
Membre
Dernière intervention
23 octobre 2009

Je n'ai pas vu ta réponse entre temps, désolé.

Il doit sûrement y'avoir un problème conceptuel (ce qui ressortait aussi de mon précédent post). Cependant, le point 3 ne peut pas s'appliquer. Je parle ici de Type Int et String, simplement pour montrer ce que mon générique peut être. Mais il y'aura d'autres types de prévus DateTime, Float, et autres sont prévus.

Le point 1, créer des attributs personnalisés : Je ne vois pas trop ce que c'est je vais donc faire quelques recherches la dessus pour voir comment je pourrais les utiliser dans mon projet. Si tu as des infos, exemples la dessus, n'hésites pas!
Le point 2, Appeller directement tes méthodes par la réflexion : pourquoi penses tu que dans mon cas ce n'est pas possible ?

Si je résume, mon DbType<T> doit absolument rester générique : Voici mon objet

class DbType<T>
    {
        private T value;
        private int size;

        private bool isPrimaryKey;
        private bool isAutoIncrement;
        private bool isForeignKey;

        public DbType(T value)
        {
            this.value = value;
            this.size = 20; // Valeur par Défaut

            isPrimaryKey = false;
            isAutoIncrement = false;
            isForeignKey = false;
        }

        public DbType(T value, int size)
        {
            this.value = value;
            this.size = size;

            isPrimaryKey = false;
            isAutoIncrement = false;
            isForeignKey = false;
        }

        public T Value
        {
            get
            {
                return this.value;
            }
            set
            {
                this.value = value;
            }
        }

        public int Size
        {
            get
            {
                return this.size;
            }
            set
            {
                this.size = value;
            }
        }

       // D'autres méthodes/propriétés non importantes ici
    }

Comment vois-tu les choses ?
Messages postés
1160
Date d'inscription
vendredi 23 juillet 2004
Statut
Membre
Dernière intervention
21 octobre 2010
18
alors pour le point 2, le fait d'appeler tes méthodes directement par reflexion va fonctionner mais ne fais que reporter le probleme : ton type de retour est tjs dans un object et tu veux son type réel (a moins que tu n'en aies pas besoin? mais j'en doute...enfin si cest le cas, tu peux utiliser le point 2).

pour les attributs personnalisés, ca serait pas utile ici (finallement) au vu de ton objet. reste la reflexion jusqu'au bout. jusqu'a quel point peux tu faire abstraction du type réel utilisé pour la généricité? la problématique est relativement simple : si tu dois utiliser explicitement ce type, la reflexion n'est que moyennement adaptée (voire pas du tout selon le cas). si tu peux ten abstraire quasiment jusqu'en bout de chaine, ca ira