[C#] Comment utiliser la réflexion pour appeler une méthode générique?


Answers

Juste un ajout à la réponse originale. Alors que cela va fonctionner:

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Il est également un peu dangereux en ce que vous perdez la vérification à la GenericMethod de GenericMethod . Si vous effectuez ultérieurement un refactoring et que vous renommez GenericMethod , ce code ne sera pas remarqué et échouera au moment de l'exécution. De même, s'il y a un post-traitement de l'assembly (par exemple, obscurcissement ou suppression de méthodes / classes inutilisées), ce code peut également être rompu.

Donc, si vous connaissez la méthode à laquelle vous vous connectez au moment de la compilation, et cela n'est pas appelé des millions de fois, les frais généraux ne sont pas importants, je changerais ce code en:

Action<> GenMethod = GenericMethod<int>;  //change int by any base type 
                                          //accepted by GenericMethod
MethodInfo method = this.GetType().GetMethod(GenMethod.Method.Name);
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Bien que pas très joli, vous avez ici une référence de GenericMethod à GenericMethod , et si vous refactorisez, supprimez ou faites quoi que ce soit avec GenericMethod , ce code continuera à fonctionner, ou au moins cassera au moment de la compilation (si par exemple vous supprimez GenericMethod ).

Une autre façon de faire la même chose serait de créer une nouvelle classe wrapper et de la créer via Activator . Je ne sais pas s'il y a un meilleur moyen.

Question

Quelle est la meilleure façon d'appeler une méthode générique lorsque le paramètre type n'est pas connu au moment de la compilation, mais est obtenu dynamiquement au moment de l'exécution?

Considérons l'exemple de code suivant - dans la méthode Example() , quelle est la manière la plus concise d'invoquer GenericMethod<T>() utilisant le Type stocké dans la variable myType ?

public class Sample
{
    public void Example(string typeName)
    {
        Type myType = FindType(typeName);

        // What goes here to call GenericMethod<T>()?
        GenericMethod<myType>(); // This doesn't work

        // What changes to call StaticMethod<T>()?
        Sample.StaticMethod<myType>(); // This also doesn't work
    }

    public void GenericMethod<T>()
    {
        // ...
    }

    public static void StaticMethod<T>()
    {
        //...
    }
}



Personne n'a fourni la solution " Reflection classique ", donc voici un exemple de code complet:

using System;
using System.Collections;
using System.Collections.Generic;

namespace DictionaryRuntime
{
    public class DynamicDictionaryFactory
    {
        /// <summary>
        /// Factory to create dynamically a generic Dictionary.
        /// </summary>
        public IDictionary CreateDynamicGenericInstance(Type keyType, Type valueType)
        {
            //Creating the Dictionary.
            Type typeDict = typeof(Dictionary<,>);

            //Creating KeyValue Type for Dictionary.
            Type[] typeArgs = { keyType, valueType };

            //Passing the Type and create Dictionary Type.
            Type genericType = typeDict.MakeGenericType(typeArgs);

            //Creating Instance for Dictionary<K,T>.
            IDictionary d = Activator.CreateInstance(genericType) as IDictionary;

            return d;

        }
    }
}

La classe DynamicDictionaryFactory ci-dessus a une méthode

CreateDynamicGenericInstance(Type keyType, Type valueType)

et il crée et renvoie une instance IDictionary, dont les types de clés et de valeurs sont exactement ceux spécifiés dans l'appel keyType et valueType .

Voici un exemple complet comment appeler cette méthode pour instancier et utiliser un Dictionary<String, int> :

using System;
using System.Collections.Generic;

namespace DynamicDictionary
{
    class Test
    {
        static void Main(string[] args)
        {
            var factory = new DictionaryRuntime.DynamicDictionaryFactory();
            var dict = factory.CreateDynamicGenericInstance(typeof(String), typeof(int));

            var typedDict = dict as Dictionary<String, int>;

            if (typedDict != null)
            {
                Console.WriteLine("Dictionary<String, int>");

                typedDict.Add("One", 1);
                typedDict.Add("Two", 2);
                typedDict.Add("Three", 3);

                foreach(var kvp in typedDict)
                {
                    Console.WriteLine("\"" + kvp.Key + "\": " + kvp.Value);
                }
            }
            else
                Console.WriteLine("null");
        }
    }
}

Lorsque l'application console ci-dessus est exécutée, nous obtenons le résultat correct et attendu:

Dictionary<String, int>
"One": 1
"Two": 2
"Three": 3



Avec C # 4.0, la réflexion n'est pas nécessaire car le DLR peut l'appeler en utilisant des types d'exécution. Puisque l'utilisation de la bibliothèque DLR est assez pénible (au lieu du code généré par le compilateur C #), le framework open source Dynamitey (.net standard 1.5) vous permet d'accéder facilement aux mêmes appels générés par le compilateur. pour toi.

var name = InvokeMemberName.Create;
Dynamic.InvokeMemberAction(this, name("GenericMethod", new[]{myType}));


var staticContext = InvokeContext.CreateStatic;
Dynamic.InvokeMemberAction(staticContext(typeof(Sample)), name("StaticMethod", new[]{myType}));



Links