Pouvez-vous obtenir un Func (ou similaire) à partir d’un object MethodInfo?

Je me rends compte que, d’une manière générale, l’utilisation de la reflection a des conséquences sur la performance. (Je ne suis pas du tout un fan de reflection, en fait, c’est une question purement académique.)

Supposons qu’il existe une classe qui ressemble à ceci:

public class MyClass { public ssortingng GetName() { return "My Name"; } } 

Ours avec moi ici. Je sais que si j’ai une instance de MyClass appelée x , je peux appeler x.GetName() . De plus, je pourrais définir une variable Func sur x.GetName .

Maintenant, voici ma question. Disons que je ne sais pas que la classe ci-dessus s’appelle MyClass ; J’ai un object, x , mais je n’ai aucune idée de ce que c’est. Je pourrais vérifier pour voir si cet object a une méthode GetName en faisant ceci:

 MethodInfo getName = x.GetType().GetMethod("GetName"); 

Supposons que getName n’est pas null. Puis-je en outre vérifier si getName.ReturnType == typeof(ssortingng) et getName.GetParameters().Length == 0 , et à ce stade, ne serais-je pas certain que la méthode représentée par mon object getName pourrait certainement être jeté dans un Func , en quelque sorte?

Je me rends compte qu’il y a un MethodInfo.Invoke , et je réalise aussi que je pourrais toujours créer un Func comme:

 Func getNameFunc = () => getName.Invoke(x, null); 

Je suppose que ce que je demande, c’est s’il y a un moyen de passer d’ un object MethodInfo à la méthode réelle qu’il représente, entraînant le coût de performance de la reflection dans le processus , mais après cela, être capable d’appeler directement , un Func ou quelque chose de similaire) sans pénalité de performance.

Ce que je vois pourrait ressembler à ceci:

 // obviously this would throw an exception if GetActualInstanceMethod returned // something that couldn't be cast to a Func Func getNameFunc = (Func)getName.GetActualInstanceMethod(x); 

(Je réalise que ça n’existe pas, je me demande s’il y a quelque chose comme ça.)

    Ce type de remplacement remplace ma réponse précédente car, bien que ce soit un itinéraire légèrement plus long, vous donne un appel rapide à la méthode et, contrairement à certaines des autres réponses, vous permet de traverser différentes instances (au cas où vous rencontreriez plusieurs instances) du même type). Si vous ne le voulez pas, vérifiez ma mise à jour en bas (ou regardez la réponse de Ben M).

    Voici une méthode de test qui fait ce que vous voulez:

     public class TestType { public ssortingng GetName() { return "hello world!"; } } [TestMethod] public void TestMethod2() { object o = new TestType(); var input = Expression.Parameter(typeof(object), "input"); var method = o.GetType().GetMethod("GetName", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); //you should check for null *and* make sure the return type is ssortingng here. Assert.IsFalse(method == null && !method.ReturnType.Equals(typeof(ssortingng))); //now build a dynamic bit of code that does this: //(object o) => ((TestType)o).GetName(); Func result = Expression.Lambda>( Expression.Call(Expression.Convert(input, o.GetType()), method), input).Comstack(); ssortingng str = result(o); Assert.AreEqual("hello world!", str); } 

    Une fois que vous avez créé le délégué une fois, vous pouvez le mettre en cache dans un dictionnaire:

     Dictionary> _methods; 

    Tout ce que vous faites est alors de l’append au dictionnaire, en utilisant le Type de l’object entrant (à partir de GetType ()) comme clé. À l’avenir, vérifiez d’abord si vous avez un délégué prêt à l’emploi dans le dictionnaire (et invoquez-le si tel est le cas), sinon vous le construirez d’abord, vous l’appendez, puis vous l’appelerez.

    Incidemment, il s’agit d’une version très simplifiée du genre de choses que le DLR fait pour son mécanisme de répartition dynamic (en termes de C #, c’est à ce moment que vous utilisez le mot clé «dynamic»).

    et enfin

    Si, comme quelques personnes l’ont mentionné, vous voulez simplement cuire un Func lié directement à l’object que vous recevez, alors vous faites ceci:

     [TestMethod] public void TestMethod3() { object o = new TestType(); var method = o.GetType().GetMethod("GetName", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); Assert.IsFalse(method == null && !method.ReturnType.Equals(typeof(ssortingng))); //this time, we bake Expression.Constant(o) in. Func result = Expression.Lambda>( Expression.Call(Expression.Constant(o), method)).Comstack(); ssortingng str = result(); //no parameter this time. Assert.AreEqual("hello world!", str); } 

    Notez cependant que, une fois l’arborescence des expressions jetée, vous devez vous assurer que l’ o rest dans la scope, sinon vous pourriez obtenir des résultats désagréables. Le moyen le plus simple serait de conserver une référence locale (dans une instance de classe, par exemple) pendant toute la durée de vie de votre délégué. (Supprimé à la suite des commentaires de Ben M)

    Oui, c’est possible:

     Func func = (Func) Delegate.CreateDelegate(typeof(Func), getName); 

    Voici ma réponse, en construisant un arbre d’expression. Contrairement aux autres réponses, le résultat ( getNameFunc ) est une fonction liée à l’instance d’origine – sans avoir à la transmettre en paramètre.

     class Program { static void Main(ssortingng[] args) { var p = new Program(); var getNameFunc = GetSsortingngReturningFunc(p, "GetName"); var name = getNameFunc(); Debug.Assert(name == p.GetName()); } public ssortingng GetName() { return "Bob"; } static Func GetSsortingngReturningFunc(object x, ssortingng methodName) { var methodInfo = x.GetType().GetMethod(methodName); if (methodInfo == null || methodInfo.ReturnType != typeof(ssortingng) || methodInfo.GetParameters().Length != 0) { throw new ArgumentException(); } var xRef = Expression.Constant(x); var callRef = Expression.Call(xRef, methodInfo); var lambda = (Expression>)Expression.Lambda(callRef); return lambda.Comstack(); } } 

    La méthode la plus simple consiste à utiliser Delegate.CreateDelegate :

     Func getNameFunc = (Func) Delegate.CreateDelegate( typeof(Func), x, getName); 

    Notez que cela lie getNameFunc à x , donc pour chaque x vous devez créer une nouvelle instance de délégué. Cette option est beaucoup moins compliquée que les exemples basés sur l’ Expression . Cependant, avec les exemples basés sur des expressions, il est possible de créer une Func getNameFuncForAny une fois, que vous pouvez réutiliser pour chaque instance de MyClass .

    Pour créer un tel getNameFuncForAny, il vous faut une méthode comme

     public Func GetInstanceMethod(MethodInfo method) { ParameterExpression x = Expression.Parameter(typeof(MyClass), "it"); return Expression.Lambda>( Expression.Call(x, method), x).Comstack(); } 

    que vous pouvez utiliser comme ça:

     Func getNameFuncForAny = GetInstanceMethod(getName); MyClass x1 = new MyClass(); MyClass x2 = new MyClass(); ssortingng result1 = getNameFuncForAny(x1); ssortingng result2 = getNameFuncForAny(x2); 

    Si vous ne voulez pas être lié à Func , vous pouvez définir

     public TDelegate GetParameterlessInstanceMethod(MethodInfo method) { ParameterExpression x = Expression.Parameter(method.ReflectedType, "it"); return Expression.Lambda( Expression.Call(x, method), x).Comstack(); } 

    Vous pouvez créer un arbre d’expression représentant un lambda appelant cette méthode, puis le Comstack() pour que les autres appels soient aussi rapides que les appels compilés standard.

    Alternativement, j’ai écrit cette méthode il y a longtemps, basée sur un article MSDN génial, qui génère un wrapper utilisant IL pour appeler tous les MethodInfo plus rapidement qu’avec MethodInfo.DynamicInvoke car une fois le code généré, il n’y a presque pas de surcharge lors d’un appel normal .

    Une approche hors du commun serait d’utiliser la dynamic. Vous pourriez alors quelque chose comme ça:

     if( /* This method can be a Func */) { dynamic methodCall = myObject; ssortingng response = methodCall.GetName(); }