[c#] Come uso la riflessione per chiamare un metodo generico?



Answers

Solo un'aggiunta alla risposta originale. Mentre questo funzionerà:

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

È anche un po 'pericoloso in quanto si perde il controllo in fase di compilazione per GenericMethod . Se in seguito si esegue un refactoring e si rinomina GenericMethod , questo codice non verrà notato e non riuscirà in fase di esecuzione. Inoltre, se è presente una post-elaborazione dell'assieme (ad esempio, occultamento o rimozione di metodi / classi non utilizzati), questo codice potrebbe interrompersi.

Quindi, se conosci il metodo a cui ti stai collegando in fase di compilazione, e questo non viene chiamato milioni di volte, quindi l'overhead non ha importanza, cambierei questo codice per essere:

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);

Anche se non molto carina, qui hai un riferimento alla compilazione di GenericMethod , e se tu rifattori, cancelli o fai qualcosa con GenericMethod , questo codice continuerà a funzionare, o almeno si interromperà al momento della compilazione (se per esempio rimuovi GenericMethod ).

Un altro modo per fare lo stesso sarebbe creare una nuova classe wrapper e crearla tramite Activator . Non so se c'è un modo migliore.

Question

Qual è il modo migliore per chiamare un metodo generico quando il parametro type non è noto al momento della compilazione, ma viene invece ottenuto dinamicamente in fase di runtime?

Considera il seguente codice di esempio: all'interno del metodo Example() , qual è il modo più conciso per richiamare GenericMethod<T>() utilizzando il Type archiviato nella variabile 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>()
    {
        //...
    }
}



Con C # 4.0, la riflessione non è necessaria in quanto la DLR può chiamarla utilizzando i tipi di runtime. Dal momento che l'uso della libreria DLR è una specie di dolore dinamico (invece del compilatore C # che genera codice per te), il framework open source Dynamitey (.net standard 1.5) offre un facile accesso in fase di esecuzione cache alle stesse chiamate che il compilatore genererebbe per te.

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


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



Nessuno ha fornito la soluzione " classic Reflection ", quindi ecco un esempio di codice completo:

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 precedente classe DynamicDictionaryFactory ha un metodo

CreateDynamicGenericInstance(Type keyType, Type valueType)

e crea e restituisce un'istanza IDictionary, i cui tipi di chiavi e valori sono esattamente specificati nella keyType chiamataTipo e keyType .

Ecco un esempio completo su come chiamare questo metodo per creare un'istanza e utilizzare 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");
        }
    }
}

Quando viene eseguita la suddetta applicazione della console, otteniamo il risultato corretto, atteso:

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



Links