Création d'un arbre d'expression qui appelle une méthode

c# delegates expression-trees lambda reflection

Question

Est-il possible de créer un arbre d'expression qui appelle directement une méthode? Par exemple, considérons la méthode suivante:

public static int MyFunc(int a, int b)
{
    return a + b;
}

J'aimerais créer un arbre d'expression qui appelle MyFunc avec les paramètres a = 1 et b = 2. Une façon d’y parvenir est de réfléchir:

public static int MyFunc(int a, int b)
{
    return a + b;
}

Cependant, ceci est désavantageux car la réflexion est lente et transforme ce qui devrait être des erreurs de compilation en erreurs d'exécution.

Je pourrais utiliser l'approche suivante à la place:

public static int MyFunc(int a, int b)
{
    return a + b;
}

Mais ce n’est toujours pas ce que je veux, car elle englobe la méthode dans une expression lambda au lieu de l’appeler directement.

Une bonne solution pourrait être basée sur un délégué, comme ceci:

public static int MyFunc(int a, int b)
{
    return a + b;
}

Malheureusement, cela ne compile pas car del est un délégué plutôt qu'une expression. Est-il possible de créer une expression à partir d'un délégué? (Notez que je connais la cible du délégué au moment de la compilation. Je n'ai donc pas besoin du type de flexibilité décrit ici: arbres d'expression et invocation d'un délégué .)

Une solution non déléguée conviendrait également, dans la mesure où elle appelle la méthode cible le plus directement possible.

Mise à jour: Cela fonctionne aussi, mais cela repose toujours sur la réflexion:

public static int MyFunc(int a, int b)
{
    return a + b;
}

Au moins, il est plus susceptible d’attraper des problèmes au moment de la compilation. Mais il paie toujours le prix de la réflexion au moment de l'exécution, n'est-ce pas?

Réponse d'expert

Tant que vous appelez. .Compile sur le lambda et stockez (et réutilisez) le délégué, vous ne payez le prix de réflexion qu'une fois.

var c1 = Expression.Constant(1);
var c2 = Expression.Constant(2);
var expr = Expression.Call(typeof(Program).GetMethod("MyFunc"), c1, c2);
Func<int> func = Expression.Lambda<Func<int>>(expr).Compile();
// ** now store func and re-use it **

Toutefois, pour obtenir un délégué nu juste cette méthode, vous pouvez utiliser:

var c1 = Expression.Constant(1);
var c2 = Expression.Constant(2);
var expr = Expression.Call(typeof(Program).GetMethod("MyFunc"), c1, c2);
Func<int> func = Expression.Lambda<Func<int>>(expr).Compile();
// ** now store func and re-use it **

bien sûr, vous êtes alors obligé de fournir les constantes à l'appelant.

Une autre option est DynamicMethod , mais tant que vous mettez en cache le dernier délégué, cela ne sera pas beaucoup plus rapide. Il offre plus de flexibilité (au prix de la complexité), mais cela ne semble pas être le problème ici.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi