Method Factory – affaire vs reflection

Je suis tombé sur du code l’autre jour et je me suis demandé si c’était la meilleure façon de le faire. Nous avons une méthode qui prend une chaîne à partir de données de formulaire Web et fait quelque chose à un object en fonction de la chaîne transmise. Actuellement, il utilise la reflection pour déterminer l’action à effectuer, mais je me demandais si une instruction switch serait préférable .

Exemple:

Edit: j’ai ajouté une troisième option pour les delegates, comme l’a noté Lucerno

public class ObjectManipulator { private void DoX(object o) { } private void DoY(object o) { } private void DoZ(object o) { } public void DoAction(ssortingng action, object o) { switch (action) { case "DoX": DoX(o); break; case "DoY": DoY(o); break; case "DoZ": DoZ(o); break; default: throw new Exception(ssortingng.Format( "Cannot locate action:{0}", action)); } } public void DoActionViaReflection(ssortingng action, object o) { MethodInfo method = typeof(ObjectManipulator). GetMethod(action, new Type[] { typeof(object) }); if (method == null) { throw new Exception(ssortingng.Format( "Cannot locate action:{0}", action)); } else { method.Invoke(this, new object[] { o }); } } private Dictionary<string, Action> _methods; public ObjectManipulator() { _methods = new Dictionary<string, Action>() { {"DoX", o => DoX(o)}, {"DoY", o => DoY(o)}, {"DoZ", o => DoZ(o)} }; } public void DoActionViaDelegates(ssortingng action, object o) { if (!_methods.ContainsKey(action)) { throw new Exception(ssortingng.Format( "Cannot locate action:{0}", action)); } else { _methods[action](o); } } } 

Le premier exemple utilise le commutateur et, comme vous pouvez le constater, peut devenir très prolixe. La seconde est beaucoup plus courte, mais utilise la reflection, ce que certaines personnes évitent comme la peste.

Une méthode fonctionnera-t-elle nettement mieux que l’autre?

La performance changerait-elle s’il y avait 100 actions différentes au lieu de 3?

Que voyez-vous plutôt dans votre code si vous le lisiez?

Le premier cas sera presque toujours plus rapide. Cependant, ses performances tiennent au fait qu’il peut être lié tôt lors de la compilation, mais c’est aussi son principal inconvénient: cette approche ne permet pas, par exemple, de manipuler des assemblys chargés de manière dynamic, et elle est beaucoup plus sujette aux erreurs impératif et non déclaratif. (Oublier une action récemment mise en œuvre, par exemple, est une chose qui pourrait arriver rapidement.)

Mon approche habituelle consiste à utiliser la reflection pour implémenter de tels modèles au moment de la découverte, mais à utiliser des delegates au moment de l’invocation. Vous bénéficiez ainsi de la souplesse de l’approche de reflection avec une performance très proche de l’approche initiale.

  • Phase de découverte: utilisez la reflection pour rechercher les membres (à l’aide d’atsortingbuts, d’interfaces, de signatures et / ou de conventions de codage). Dans votre cas, vous avez toujours la même signature, le délégué à utiliser serait donc Action . Ajoutez ces membres à une instance Dictionary> , en créant un délégué à partir de MethodInfo aide de CreateDelegate() .

  • Phase d’invocation: récupère le délégué via sa clé et l’invoque, ce qui est très simple (en supposant que le dictionnaire s’appelle methods ): methods[action](o)

La performance ne devrait pas vous préoccuper, sauf si vous la décrivez et que c’est un goulot d’étranglement. Le fait le plus important, IMO, est que vous perdez la sécurité et l’parsing de type statique dans la version avec reflection. Au moment de la compilation, il n’ya aucun moyen de vérifier si ces méthodes d’action DoX , DOY , etc. sont appelées. Cela peut ou peut ne pas être un problème pour vous, mais ce serait ma plus grande préoccupation.

De plus, le nombre d’actions n’est absolument pas pertinent pour l’exécution de la version de reflection. GetMethod ne ralentit pas lorsque vous avez beaucoup de membres dans votre classe.

Vous pouvez corriger la réponse de @ user287107 comme suit:

 var Actions = new Dictionary>(); Actions["DoX"] = DoX; Actions["DoY"] = DoY; Actions["DoZ"] = DoZ; 

Il s’agit en fait de la phase de découverte de la réponse de @ Lucero explicitement, ce qui peut être utile si les noms ne correspondent pas toujours.

Définir l’ensemble des actions dans une énumération serait également utile si possible – ce serait un peu plus rapide car vous n’auriez pas besoin de hacher les chaînes. Cela vous permettrait également d’écrire un test unitaire pour vous assurer que vous n’avez manqué aucune valeur potentielle.

que diriez-vous d’utiliser des delegates? vous pourriez utiliser quelque chose comme ça:

 var Actions = new Dictionary>(); Actions["DoX"] = x => DoX(x); Actions["DoY"] = x => DoY(x); Actions["DoZ"] = x => DoZ(x); 

et ensuite

 public void DoAction(ssortingng action, object o) { Actions[action](o); } 

Je suppose que cela fonctionne mieux, si vous avez beaucoup de cas, parce que le dictionnaire a un hachage.

le type de reflection que vous avez utilisé peut créer des problèmes de sécurité, si l’utilisateur peut donner un autre nom de fonction