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 :

[C#] Instanciation dynamique d'une classe générique [FAQ]


Sujet :

C#

  1. #1
    Membre confirmé Avatar de NeoMan
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    171
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2002
    Messages : 171
    Par défaut [C#] Instanciation dynamique d'une classe générique
    Bonjour à tous !

    En connaissant le nom d'un type simple il est très simple d'en faire une instance :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
            private static object CreateInstance(string typeName)
            {
                foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                    foreach (Type type in assembly.GetTypes())
                        if ((typeName == type.Name) || (typeName == type.FullName))
                            return Activator.CreateInstance(type);
                return null;
            }

    Mais admettons que typeName désigne une classe générique...
    Par exemple :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
            string typeName = "System.Collections.Generic.List<System.String>";

    Comment peut-on l'instancier ?

    Il faut savoir que pour en déterminé l'assembly (afin d'en détermine le Type), il faudra transformer la valeur en System.Collections.Generic.List`1.
    Ok, jusque là tout va bien : de la simple chaîne de caractères on vient d'en déduire le type. Mais évidement, dans l'état actuel, Activator.CreateInstance ne peut absolument rien faire : nous n'avons pas définit qu'il s'agissait d'une liste de System.String.

    Savez-vous (planter des choux... euh non; désolé ) comment définir la liste des "Arguments génériques", afin de procéder à l'instanciation ?

    @++

    NeoMan

    PS : pour info, si vous désirez connaître les arguments génériques d'une instance déjà existante il suffit de faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Type[] argsGenerics = monInstance.GetType().GetGenericArguments();

  2. #2
    Membre Expert
    Avatar de Mehdi Feki
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    1 113
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 1 113
    Par défaut
    Salut.

    Source inspirée de How to: Examine and Instantiate Generic Types with Reflection

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
                Type d1 = typeof(System.Collections.Generic.List<>);
                Type[] typeArgs = { typeof(string) };
                Type constructed = d1.MakeGenericType(typeArgs);
                return Activator.CreateInstance(constructed);

  3. #3
    Membre confirmé Avatar de NeoMan
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    171
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2002
    Messages : 171
    Par défaut
    Oups ! J'ai ripé et j'ai cliqué sur "résolu" au lieu d'ajouter une réponse... Mais de toute façon je pense que c'est belle et bien résolu
    Je testerais ceci demain ou après-demain et je vous tiendrais au courant.
    Mais à première c'est exactement cette méthode qu'il faut utiliser !
    Merci Mehdi !

  4. #4
    Membre confirmé Avatar de NeoMan
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    171
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2002
    Messages : 171
    Par défaut
    Bon finalement j'ai pas pu résisté j'ai testé ça cet après-midi : Ca marche super bien !

    Voici le code complet pour instancier une classe générique ou non :

    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
     
            // Instancie une classe générique ou non
            public object CreateInstance(string typeName, params object[] args)
            {
                return Activator.CreateInstance(StringToTypeGeneric(typeName), args);
            }
     
            // Détermine le type exact d'une classe générique à partir du nom
            private Type StringToTypeGeneric(string typeName)
            {
                if (String.IsNullOrEmpty(typeName))
                    throw new ArgumentException("typeName is null or empty", "typeName");   
     
                string[] types = typeName.Replace(" ", "").Replace(">", "").Split('<', ',');
                bool isGenericClass = (types.Length > 1);         
     
                if (isGenericClass)
                {
                    Type typeBase = StringToType(String.Format("{0}`{1}", types[0], types.Length - 1));
                    List<Type> genericTypes = new List<Type>();
                    for (int i = 1; i < types.Length; i++)
                        genericTypes.Add(StringToType(types[i]));
                    return typeBase.MakeGenericType(genericTypes.ToArray());
                }
                else
                    return StringToType(typeName);
            }
     
            // Recherche le type à partir du nom
            private Type StringToType(string typeName)
            {
                foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                    foreach (Type type in assembly.GetTypes())
                        if ((type.Name == typeName) || (type.FullName == typeName))
                            return type;
                throw new ArgumentException(String.Format("Type '{0}' unknown !", typeName), "typeName");
            }
    Ci vous avez des suggestions d'amélioration pour ce code n'hésitez pas !

    @++

    NeoMan

  5. #5
    Membre confirmé Avatar de NeoMan
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    171
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2002
    Messages : 171
    Par défaut
    Bien je viens de refaire le code que je proposais hier.
    • J'ai amélioré la structure en utilisant la récursivité; c'est beaucoup mieux ainsi !!
    • Le fonctionnel a été revu; en effet avant on ne pouvait instancier de classe générique utilisant des classes génériques (ex: ClasseGenericA<ClasseGenericB<string, int, ClasseGenericC<object>>, int>); maintenant c'est possible.


    Voici le code actuel :
    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
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
     
            public object CreateInstance(string typeName, params object[] args)
            {
                return Activator.CreateInstance(StringToType(typeName), args);
            }
     
            public Type StringToType(string typeName)
            {
                if (String.IsNullOrEmpty(typeName))
                    throw new ArgumentException("typeName is null or empty", "typeName");
     
                typeName = typeName.Replace(" ", "");
                int indexOf = typeName.IndexOf("<");
     
                // S'il ne s'agit pas d'une classe générique
                if (indexOf < 0)
                {
                    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                        foreach (Type type in assembly.GetTypes())
                            if ((type.Name == typeName) || (type.FullName == typeName))
                                return type;
                    throw new ArgumentException(String.Format("Type '{0}' unknown !", typeName), "typeName");
                }
                // sinon on détail l'interprétation du nom
                else
                {
                    string typeBaseName = typeName.Substring(0, indexOf);
                    string[] argumentsGeneric = typeName.Substring(indexOf + 1).Remove(typeName.Length - indexOf - 2).Split(',');
                    List<Type> typeGenerics = new List<Type>();
                    string currentArgument = "";
                    int countLevelGeneric = 0;
                    foreach (string argument in argumentsGeneric)
                    {
                        foreach (char car in argument)
                            switch (car)
                            {
                                case '<': countLevelGeneric++; break;
                                case '>': countLevelGeneric--; break;
                            }
                        currentArgument += argument;
                        if (countLevelGeneric == 0)
                        {
                            typeGenerics.Add(StringToType(currentArgument));
                            currentArgument = "";
                        }
                    }
                    Type typeBase = StringToType(String.Format("{0}`{1}", typeBaseName, typeGenerics.Count));
                    return typeBase.MakeGenericType(typeGenerics.ToArray());
                }
            }
    N'hésitez pas si vous avez des remarques,

    @++

    NeoMan

  6. #6
    Rédacteur
    Avatar de Thomas Lebrun
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    9 161
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 9 161
    Par défaut
    Voila qui mérite d'être présent dans la FAQ

    En tout cas bravo

  7. #7
    Membre confirmé Avatar de NeoMan
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    171
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2002
    Messages : 171
    Par défaut
    Merci merci


  8. #8
    Membre actif
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    47
    Détails du profil
    Informations personnelles :
    Localisation : Indonésie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 47
    Par défaut Question sur l'utilisation objet dynamique
    Citation Envoyé par NeoMan Voir le message
    Bonjour à tous !

    En connaissant le nom d'un type simple il est très simple d'en faire une instance :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
            private static object CreateInstance(string typeName)
            {
                foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                    foreach (Type type in assembly.GetTypes())
                        if ((typeName == type.Name) || (typeName == type.FullName))
                            return Activator.CreateInstance(type);
                return null;
            }
    Bonjour, j'ai une question à vous poser.
    Comment pour que je puisse utiliser l'objet instancier return Activator.CreateInstance(type); que j'ai récupéré par un autre objet de type Object ? Par exemple , j'ai envie d'appeler un function qui se trouve dans un autre Classe. Merci d'avance
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Object obj;
    
    Tools objTool = new Tools();
    obj = objTool.CreateInstance("Electric");
    
    obj. ????

  9. #9
    Membre confirmé Avatar de NeoMan
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    171
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2002
    Messages : 171
    Par défaut
    Réponse courte...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    Object obj;
     
    Tools objTool = new Tools();
    obj = objTool.CreateInstance("Electric");
     
    Object result = obj.GetType().GetMethod("MaMethdode").Invoke(obj, new Object[] { "paramètre 1", "paramètre 2" });
    Biensûr idéalement, il faudrait récupéré le type de l'objet via Tools (à la place de refaire un GetType par la suite); vérifier que GetMethod ne retourne pas null (voir passé des BindingsFlags à GetMethod pour apporter des contraintes de sélection); etc...

    A noté que depuis le .NET 4.0 il y a le mot clé dynamic qui intéressant dans ce cas. Je te conseil de creuser ça si tu es en .NET 4.0

    @+

  10. #10
    Membre actif
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    47
    Détails du profil
    Informations personnelles :
    Localisation : Indonésie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 47
    Par défaut
    Je ne comprends pas avec cette ligne de code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Object result = obj.GetType().GetMethod("MaMethdode").Invoke(obj, new Object[] { "paramètre 1", "paramètre 2" });
    J'ai essayé de le faire mais ça ne marche pas. Voici ce que j'ai ecris:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Object result = objObject.GetType().GetMethod("setStarted").Invoke(objObject, new Object[] { "paramètre 1", "paramètre 2" });
    Mon function de setStarted se trouve dans la classe de Base et la classe Tools est une classe derivée de la classe de Base.
    Qu'est-ce-que je dois mettre dans "paramètre 1" et "paramètre 2" ?
    Pouvez-vous m'expliquer en détails s.v.p.

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

Discussions similaires

  1. C++ builder prb d instanciation dynamique d une class
    Par roindesbois dans le forum C++Builder
    Réponses: 2
    Dernier message: 25/01/2014, 20h58
  2. Réponses: 2
    Dernier message: 05/12/2013, 12h15
  3. Instanciation dynamique d'une classe
    Par larnin_ dans le forum Langage
    Réponses: 3
    Dernier message: 23/03/2013, 16h24
  4. [ClassLoader] Chargement dynamique d'une classe -> problème avec packages !
    Par ymerej dans le forum API standards et tierces
    Réponses: 9
    Dernier message: 31/05/2006, 21h37
  5. [RegEx] Trouver les appels statique et dynamique d'une class
    Par jeff_! dans le forum Langage
    Réponses: 8
    Dernier message: 07/04/2006, 16h31

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