IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Passage dynamique de types a une fonction générique


Sujet :

C#

  1. #1
    Membre averti
    Inscrit en
    Août 2008
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Août 2008
    Messages : 20
    Par défaut Passage dynamique de types a une fonction générique
    Bonjour tout le monde !

    J'ai une fonction générique qui doit accepter différents types :

    voila ma la forme générale de ma fonction générique :

    private void GenericFunction<T>(T UnType)
    {
    IContainer comp=UnType.components;
    }
    Je veut faire passer des types a cette fonction, mais mon problème que je connait le type qu'au moment de l'exécution. Voici ce que je fait exactement lors de l'appel de cette fonction:

    string MonType = this.ActiveMdiChild.GetType().ToString(); //Récupérer le type
    GenericFunction<MonType> (this.ActiveMdiChild); //Appel de la fonction
    J'explique : je récupère le type dans une chaîne de caractères "MonType" et j'appelle la fonction générique en lui donnant le type et je lui passe en paramètre l'instance du type en question (dans mon cas c'est la forme active de ma fenêtre MDI).
    Mais cela ne marche pas : erreur de compilation.

    Rq : Les types que j'utilise sont des classes qui héritent de Form, le cating de ces classes au type Form ne résoud pas mon problème parceque je doit récupérer une instance de l'attribut "components" qui est spécifique a ces types et ne se trouve pas dans le type Form.

    Je suis bloqué sur ce problème y a plusieurs jours , besoin de votre d'aide.

  2. #2
    Rédacteur
    Avatar de dev01
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    2 451
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2004
    Messages : 2 451
    Par défaut
    Salut.

    En fait ton problème telque que tu le présentes est non soluble.
    En effet tu confond ce qui est du domaine du runtime
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    string MonType = this.ActiveMdiChild.GetType().ToString(); //Récupérer le type
    et ce qui est de la partie de la compilation
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    GenericFunction<MonType> (this.ActiveMdiChild); //Appel de la fonction
    Le plus simple c'est encore de définir une interface correspondant à ta propriété Components et de faire implémenter cette interface à tout les types enfant.

    ainsi tu aura le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    private void GenericFunction(IMonType UnType)
    {
    IContainer comp=UnType.components;
    }

  3. #3
    Expert confirmé
    Avatar de smyley
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    6 270
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 6 270
    Par défaut
    On a fait exprès Type.MakeGenericType pour ça ...

  4. #4
    Membre averti
    Inscrit en
    Août 2008
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Août 2008
    Messages : 20
    Par défaut
    Merci pour les réponses, c vrai que créer une interface peut résoudre mon problème, mais dans mon cas je peut pas recourir a cette solution parce que je peut pas modifier les classes des types que j'utilise. En faite je suis en train de développer un composant qui doit être utilisé dans d'autres application C# sans modifier leur code, donc je doit utiliser ces types sans les modifier.

    S'il y a d'autres façons de faire je suis preneur, je vous remercie d'avance pour votre aide.

  5. #5
    Membre averti
    Inscrit en
    Août 2008
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Août 2008
    Messages : 20
    Par défaut
    bon j'ai trouvé une autre façon pour résoudre mon problème, il s'agit de la réflexion, j'ai fait une méthode qui permet d'exécuter du code passé sous forme de chaîne en paramètre, sa doit marcher mais mon seul problème c'est dans l'utilisation de la méthode invoke, je passe this comme paramètre a cette méthode mais j'obtient une erreur m'indiquant que la référence n'est pas définie a une instance d'un objet, est ce qu'il y a qq chose qui cloche avec mon code :

    voici ma fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
     
    public Object Execute(string code)
            {
                CodeDomProvider cc = new CSharpCodeProvider();
                CompilerParameters cp = new CompilerParameters();
                cp.ReferencedAssemblies.Add("System.dll");
     
                string ClassPath = Application.StartupPath;
                ClassPath = ClassPath.Substring(0, ClassPath.LastIndexOf("\\"));
                ClassPath = ClassPath.Substring(0, ClassPath.LastIndexOf("\\"));
     
                string toCompile = "using System;\r\nusing System.Windows.Forms;\nusing System.ComponentModel;\r\nnamespace AppGest\r\n{\r\n\tpublic class ClassTmp\r\n\t{\r\n\t\t" + code + "\r\n\r\n\t}\r\n}";
                MessageBox.Show(toCompile);
                CompilerResults cr = cc.CompileAssemblyFromSource(cp, toCompile);
     
                if (cr.Errors.HasErrors)
                {
                    String text = "Il y a eu des erreurs dans la compilation d'un script :\n";
                    foreach (CompilerError err in cr.Errors)
                        text += err + "\n";
                    MessageBox.Show(text);
                    return null;
                }
     
                Assembly a = cr.CompiledAssembly;
     
                Type classTmp = a.GetType("ClassTmp"); // Renvoi NULL !
     
                Module[] modules = a.GetModules();
                Type[] types = modules[0].GetTypes();
                foreach (Type type in types)
                {
                    MethodInfo mi = type.GetMethod("FunctionTmp");
                    MessageBox.Show(mi.ToString() + " " + type.ToString());
                    object[] args = { this };
                    mi.Invoke(type, args);
                }
          }
    et voici l'appel a cette fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
                    string frmtype = this.ActiveMdiChild.GetType().ToString();
                    frmtype=frmtype.Substring(frmtype.IndexOf('.')+1);
                    string ClassPath=Application.StartupPath;
                    ClassPath=ClassPath.Substring(0,ClassPath.LastIndexOf("\\"));
                    ClassPath=ClassPath.Substring(0, ClassPath.LastIndexOf("\\"));
     
                    string code = "public void FunctionTmp (Form parent)\n{\n" + frmtype + " frm=parent.ActivateMdiChild;\nIContainer comp = frm.components;\nExport exp = new Export(comp);\n}";
                    Execute(code);
    Aidez moi SVP

  6. #6
    Expert confirmé
    Avatar de smyley
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    6 270
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 6 270
    Par défaut
    Et tu as essayé de regarder les exemples de Type.MakeGenericType ? c'est exactement ce que tu veux faire (en 12 fois moins de lignes).

  7. #7
    Membre averti
    Inscrit en
    Août 2008
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Août 2008
    Messages : 20
    Par défaut
    le lien n'a pas marché au début, mais maintenant sa va, je vais voir cette solution, si je comprend pas qq chose je vais vous demander de l'aide

  8. #8
    Membre averti
    Inscrit en
    Août 2008
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Août 2008
    Messages : 20
    Par défaut
    j'ai bien lu ton exemple smyley, mais je sais pas comment sa pourrait me servir dans mon problème (appel d'une méthode générique avec un type inconnu dans la compilation), je vous prie de m'expliquer avec un petit exemple comment je peut utiliser la méthode MakeGenericType() dans mon cas. Merci beaucoup.

  9. #9
    Expert confirmé
    Avatar de smyley
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    6 270
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 6 270
    Par défaut
    Ah mrd, c'est MakeGenericMethod (je devais être fatigué).
    Exemple :
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    public virtual IDatabaseValueTypable GetTypeDescription(Type t)
            {
                Type this_t = typeof(DatabaseValueTypeManager);
                MethodInfo info = this_t.GetMethod("GetTypeDescription", new Type[0]);
                MethodInfo g_info = info.MakeGenericMethod(t);
                return (IDatabaseValueTypable)g_info.Invoke(this, new object[0]);
            }
     
            public virtual DatabaseValueType<T> GetTypeDescription<T>()
            {
                 ....
             }

  10. #10
    Membre averti
    Inscrit en
    Août 2008
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Août 2008
    Messages : 20
    Par défaut
    sa rassemble a la solution avec réflexion mais cette fois on combine généricité et réflexion. Mais j'ai toujours des erreurs de compilation :

    J'ai écrit la classe générique suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    using System;
    using System.ComponentModel;
     
    namespace AppGest
    {
        public class GenericForm
        {
     
            public void AppelExport<T>(T frm)
    		{
    			IContainer comp = frm.components;  //ERREUR : 'T' does not contain a definition for 'components'	
    			Export exp = new Export(comp);
    		}
        }
    }
    Et voici l'appel :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    Type ex = typeof(GenericForm);
    MethodInfo mi = ex.GetMethod("AppelExport");
     
    DisplayGenericMethodInfo(mi);
     
    MethodInfo miConstructed = mi.MakeGenericMethod(typeof(this.ActiveMdiChild)); //ERREUR : Type expected
    DisplayGenericMethodInfo(miConstructed);
     
    // Invoke the method.
    object[] args = { this.ActiveMdiChild };
    miConstructed.Invoke(null, args);
    Mon problème c'est les 2 erreurs de compilation que j'obtient dans les lignes indiquée ci-dessus.

  11. #11
    Rédacteur
    Avatar de dev01
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    2 451
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2004
    Messages : 2 451
    Par défaut
    Je comprend vraiment pas l'interet de faire la reflexion pour appeler AppelExport ni l'interet de la généricite de la fonction d'ailleur...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
     
    public void AppelExport(Form frm)
    {
          PropertyInfo pi = frm.GetType().GetProperty("components");
          IContainer container = (IContainer)pi.GetGetMethod().Invoke(frm, new object[] { } );
    }
    aux erreurs de frappe et à la gestion des exceptions pret c'est pas plus complique.

    Ensuite ce que je capte pas c'est ou est défini la méthode AppelExport ...

  12. #12
    Membre averti
    Inscrit en
    Août 2008
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Août 2008
    Messages : 20
    Par défaut
    Sa a l'air bien cette solution, je vais la tester.
    Pour la méthode AppelExport elle est définie dans la classe GenericForm

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    using System;
    using System.ComponentModel;
     
    namespace AppGest
    {
        public class GenericForm
        {
     
            public void AppelExport<T>(T frm)
    		{
    			IContainer comp = frm.components;
    			Export exp = new Export(comp);
    		}
        }
    }

  13. #13
    Membre averti
    Inscrit en
    Août 2008
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Août 2008
    Messages : 20
    Par défaut
    J'ai testé l'idée de dev01 mais y a un problème lors de l'exécution.
    Voici le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    public void AppelExport(Form frm)
            {
                PropertyInfo pi = frm.GetType().GetProperty("components");
                IContainer comp = (IContainer)pi.GetGetMethod().Invoke(frm, new object[] { });
            }
    Le problème est que la valeur de pi est null au moment de l'exécution, j'indique que components est un attribut public.
    Tout ce que je veux c'est accéder a cet attribut a partir de la form principale MDI, pour le faire passer comme paramètre a une classe Export que j'ai développé.

  14. #14
    Expert confirmé
    Avatar de smyley
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    6 270
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 6 270
    Par défaut
    Citation Envoyé par dev01 Voir le message
    Je comprend vraiment pas l'interet de faire la reflexion pour appeler AppelExport ni l'interet de la généricite de la fonction d'ailleur...
    Bah j'ai l'impression qu'on peut y arriver avec .... ou du moins c'est ce à quoi me fait penser la question ...

    Sinon ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    public void AppelExport<T>(T frm)
    		{
    			IContainer comp = frm.components;  //ERREUR : 'T' does not contain a definition for 'components'	
    			Export exp = new Export(comp);
    		}
    On peut s'en sortir avec des contraintes
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    public void AppelExport<T>(T frm) where T:UneInterface
    		{
    			IContainer comp = frm.components; 
    			Export exp = new Export(comp);
    		}
    Avec "UneInterface" tu peut forcer T à implémenter une certaine interface (et donc les membres qu'elle contient).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    MethodInfo miConstructed = mi.MakeGenericMethod(typeof(this.ActiveMdiChild)); //ERREUR : Type expected
    Il faut un type
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    MethodInfo miConstructed = mi.MakeGenericMethod(typeof(this.ActiveMdiChild.GetType()));
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    PropertyInfo pi = frm.GetType().GetProperty("components");
    Ce serai pas plutôt "Components" (avec un C majuscule ?)

  15. #15
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    100 balles que components n'est pas une propriété mais un champ
    Si c'est ça, le principe est le même que dans le code du post #13, mais avec GetField à la place de GetProperty :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    public void AppelExport<T>(T frm)
    {
        FieldInfo fi = frm.GetType().GetField("components");
        IContainer comp = (IContainer)fi.GetValue(frm);
    }
    Au passage, pour la propriété, ce n'était pas nécessaire de passer par GetGetMethod, il y a une méthode GetValue qui est plus simple à utiliser
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public void AppelExport(Form frm)
    {
          PropertyInfo pi = frm.GetType().GetProperty("components");
          //IContainer container = (IContainer)pi.GetGetMethod().Invoke(frm, new object[] { } );
          IContainer container = (IContainer)pi.GetValue(frm, null);
    }

  16. #16
    Membre averti
    Inscrit en
    Août 2008
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Août 2008
    Messages : 20
    Par défaut
    merci tomlev, votre solution marche de merveille
    j'ai même pas eu besoin de la généricité, voila ce que j'ai fait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    public void AppelExport(Form frm)
            {   FieldInfo fi = frm.GetType().GetField("components");
                IContainer comp = (IContainer)fi.GetValue(frm);
                Export exp = new Export(comp);
            }
    Comme sa on peut récupérer uniquement les champs publics. Mais "components" est un champs privé par défaut, pour pouvoir le récupérer avec la méthode GetField je doit le rendre public, mais je préfère ne pas toucher a ces forms comme j'ai dit dans mes anciens posts. Voila comment je fait pour pouvoir récupérer ce champ sans modifier son niveau de protection.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    public void AppelExport(Form frm)
            {   FieldInfo fi = frm.GetType().GetField("components",BindingFlags.NonPublic | BindingFlags.Instance);
                IContainer comp = (IContainer)fi.GetValue(frm);
                Export exp = new Export(comp);
            }
    Merci pour vous tous

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 15/07/2009, 19h44
  2. Réponses: 8
    Dernier message: 08/10/2008, 10h58
  3. créer un type dans une fonction javascript
    Par amelhog dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 11/07/2005, 13h54
  4. tableau dynamique en parametre d'une fonction
    Par drinkmilk dans le forum ASP
    Réponses: 4
    Dernier message: 27/04/2004, 16h35
  5. passage de tableau 2D a une fonction
    Par watashinoitadakimasu dans le forum C
    Réponses: 2
    Dernier message: 11/09/2003, 02h33

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo