Obtenez la meilleure surcharge correspondante d’un ensemble de surcharges

Disons que j’ai un cours comme suit:

public class AcceptMethods { public int Accept(ssortingng s, int k = 1) { return 1; } public int Accept(object s) { return 2; } public int Accept(IEnumerable s) { return 7; } public int Accept(IList s) { return 4; } } 

Maintenant, si j’essaye de consumr ceci en code, j’utilise quelque chose comme ceci:

  object[] list = new object[] { "a", new object[0], "c", "d" }; Assert.AreEqual(7, list.Select((a)=>((int)new AcceptMethods().Accept((dynamic)a))).Sum()); 

La raison pour laquelle il s’agit de 7, c’est parce que la résolution de surcharge préfère [ IList ] à [ IEnumerable ] et [ object ], et parce que [ ssortingng , int=default ] a la préférence sur [ object ].

Dans mon scénario, j’aimerais obtenir la meilleure surcharge correspondante en utilisant la reflection. En d’autres termes: “le meilleur” est défini comme “la résolution de la surcharge c #“. Par exemple:

 int sum = 0; foreach (var item in list) { var method = GetBestMatching(typeof(AcceptMethods).GetMethods(), item.GetType()); sum += (int)method.Invoke(myObject, new object[]{item}); } Assert.AreEqual(7, sum); 

Bien que le scénario que je dessine ne comporte qu’un paramètre, la solution que je recherche peut avoir plusieurs parameters.

Mise à jour 1 :

Étant donné que j’ai reçu un commentaire selon lequel cela est trop difficile pour SO, en raison des difficultés d’application de la résolution de surcharge (que je connais bien), je suis enclin à envoyer une mise à jour. Pour donner un certain pouvoir à mon argument, il s’agissait de ma première tentative, qui utilise le classeur .NET par défaut qui gère la résolution de la surcharge:

  private MethodBase GetBestMatching(IEnumerable methods, Type[] parameters) { return Type.DefaultBinder.SelectMethod(BindingFlags.Instance | BindingFlags.Public | BindingFlags.OptionalParamBinding | BindingFlags.InvokeMethod, methods.ToArray(), parameters, null); } 

Cette version semble déjà résoudre correctement la surcharge simple , mais ne fonctionne pas avec les parameters facultatifs. Parce que .NET afaik fonctionne avec une liaison de caractères comme je le montre ici, je suppose que la solution pourrait être mise en œuvre assez facilement.

C’est un sujet énorme , qui demande beaucoup de travail et qui, à mon avis, ne peut certainement pas être résumé. Je vous suggère de lire la spécification C # et de lire les règles formelles définissant la résolution de la surcharge (également, portez votre attention sur les méthodes génériques) et essayez de les mettre en œuvre jusqu’au point qui répond à vos besoins.

Mettre à jour

Les parameters facultatifs (c’est-à-dire les parameters avec les valeurs par défaut) ne constituent pas un cas sortingvial – et le classeur Reflection n’essaie absolument pas de les remplir – c’est parce que le compilateur doit identifier les valeurs par défaut, les extraire et les injecter dans un appel une telle méthode.

Vous avez besoin d’une approche multi-passes ressemblant à ceci (remarque – n’inclut PAS les génériques):

  1. Recherchez manuellement une méthode dont le nombre de parameters et les types de ces parameters correspondent exactement au nombre et aux types d’arguments que vous avez. Si vous trouvez une correspondance, utilisez-la et sortez-vous.

  2. Identifiez maintenant la “liste des candidats” des méthodes pour votre sélection de surcharge (généralement c’est par nom – vous pouvez également exclure les génériques ici – à moins que vous n’essayiez de les lier aussi).

  3. Si aucune de ces méthodes n’a de parameters facultatifs, vous pourrez peut-être utiliser le classeur par défaut correspondant à votre question pour trouver la correspondance (sinon, vous avez besoin d’un algorithme de classement des arguments / parameters basé sur les types – si oui, passez à 5).

  4. Relancez la liste de candidats intégrée dans 3), extrayez tous les parameters facultatifs et incorporez leurs valeurs par défaut à vos propres listes de parameters (vous devrez peut-être créer un jeu de parameters distinct pour chaque méthode à ce stade, y compris ceux que vous avez définis. ont été fournis, mais aussi les défauts).

  5. Exécutez votre algorithme de classement pour ces méthodes intégrées dans 3) et éventuellement 4) pour identifier la meilleure correspondance (vous semblez bien maîsortingser tout cela, je ne vais donc pas tout passer en revue ici. Ce n’est pas un algorithme simple et franchement, je ne peux pas tout citer mot pour mot ici non plus!).

  6. Votre algorithme de classement doit produire une méthode gagnante claire – c’est-à-dire avec un score élevé unique ou similaire. Si vous obtenez un gagnant clair, c’est celui que vous liez. Sinon, vous avez une ambiguïté et vous devez vous en sortir.

Mon propre responsable de la sécurité pourrait vous intéresser à ce stade – Paramètres et reflection par défaut: si ParameterInfo.IsOptional est-il alors que DefaultValue est toujours fiable? – qui devrait vous aider à identifier les méthodes qui ont des parameters avec des valeurs par défaut et à les éliminer.

Pour les autres personnes souhaitant résoudre le problème de surcharge d’exécution, il s’agit d’une description assez complète de la manière de l’implémenter.

Remarque importante: l’astuce “dynamic” ne s’applique pas non plus dans tous les scénarios (en particulier: les médicaments génériques); il semble que le compilateur est plus flexible que le comportement d’exécution.

Notez également qu’il ne s’agit pas d’un algorithme / implémentation complet (ou du moins, je pense que ce n’est pas le cas), mais qu’il fonctionne dans la plupart des cas, même néanmoins. J’ai trouvé que cela fonctionnait dans tous les cas que j’avais rencontrés jusqu’à présent, y compris dans des cas difficiles comme la covariance de tableaux.

L’algorithme de scoring fonctionne comme suit:

  • Si type de paramètre == type de source: score = 0
  • Si le paramètre est un argument de type générique valide (contraintes génériques): score = 1
  • Si le type de source est implicitement convertible en type de paramètre: score = 2 (voir: http://msdn.microsoft.com/en-us/library/y5b434w4.aspx pour toutes les règles).
  • Si vous devez renseigner un paramètre par défaut: score = 3
  • Sinon, calculez le score de compatibilité comme indiqué ci-dessous

Le score de compatibilité est la conversion la plus ssortingcte entre un type de type A et un type de type B (y compris covariance, contravariance). Par exemple, ssortingng [] a 1 conversion à IList (en utilisant object [] puis IList) et 2 conversions à IEnumerable (1. en utilisant object [] puis IEnumerable ou 2. en IEnumerable). Par conséquent, IList est la conversion la plus ssortingcte et est donc sélectionné.

Compter le nombre de conversions est facile:

  int cnt = CountCompatible(parameter.ParameterType, sourceType.GetInterfaces()) + CountCompatible(parameter.ParameterType, sourceType.GetBaseTypes()) + CountCompatible(parameter.ParameterType, new Type[] { sourceType }); [...] private static int CountCompatible(Type dst, IEnumerable types) { int cnt = 0; foreach (var t in types) { if (dst.IsAssignableFrom(t)) { ++cnt; } } return cnt; } 

Pour m’assurer qu’un meilleur score est sélectionné lorsqu’une conversion plus ssortingcte est utilisée, je calcule le score comme suit: ‘score = 5 – 1.0 / (cnt + 2);’. Le +2 vous assure de ne jamais diviser par 0 ou 1, ce qui donne un score entre 4 et 5

Pour résoudre le problème de surcharge, sélectionnez la méthode avec le score minimum pour tous les arguments. Assurez-vous de saisir correctement les arguments de méthode par défaut lors de l’appel (voir l’excellent lien d’Andras ci-dessus) et assurez-vous de renseigner les arguments génériques avant de renvoyer la méthode. Si vous rencontrez un tirage au sort pour la meilleure méthode: la meilleure solution consiste à lever une exception.

Au cas où vous vous le demanderiez, oui … il faut pas mal de travail pour que tout fonctionne correctement … Je prévois de rendre une version fonctionnelle disponible dans mon framework une fois que ce sera fait. (Vous verrez le moment où mon profil a un lien vers un site Web actif :-))