Surcharge, inférence de type générique et mot clé 'params'
-
10-07-2019 - |
Question
Je viens de remarquer un comportement étrange avec une résolution de surcharge.
Supposons que j'ai la méthode suivante:
public static void DoSomething<T>(IEnumerable<T> items)
{
// Whatever
// For debugging
Console.WriteLine("DoSomething<T>(IEnumerable<T> items)");
}
Maintenant, je sais que cette méthode sera souvent appelée avec un petit nombre d'arguments explicites. C'est pourquoi, par commodité, j'ajoute cette surcharge:
public static void DoSomething<T>(params T[] items)
{
// Whatever
// For debugging
Console.WriteLine("DoSomething<T>(params T[] items)");
}
J'essaie maintenant d'appeler ces méthodes:
var items = new List<string> { "foo", "bar" };
DoSomething(items);
DoSomething("foo", "bar");
Mais dans les deux cas, la surcharge avec params
est appelée. Je me serais attendu à ce que la IEnumerable<T>
surcharge soit appelée dans le cas d'un List<T>
, car cela semble mieux correspondre (du moins pour moi).
Ce comportement est-il normal? Quelqu'un pourrait-il l'expliquer? Je ne trouvais aucune information claire à ce sujet dans les documents MSDN ... Quelles sont les règles de résolution de surcharge impliquées ici?
La solution
La section 7.4.3 de la spécification C # 3.0 est le bit pertinent ici. Fondamentalement, le tableau de paramètres est développé, vous comparez donc:
public static void DoSomething<T>(T item)
et
public static void DoSomething<T>(IEnumerable<T> item)
Le T
pour la première correspondance est List<string>
et le string
pour la deuxième correspondance IEnumerable<string>
.
Considérons maintenant les conversions impliquées pour l'argument du type de paramètre. Dans le premier cas, il s'agit de <=> à <=>; dans la seconde c'est <=> à <=>. La première conversion est meilleure que la seconde selon les règles de 7.4.3.4.
Le bit contre-intuitif est l'inférence de type. Si vous retirez cela de l'équation, cela fonctionnera comme prévu:
var items = new List<string> { "foo", "bar" };
DoSomething<string>(items);
DoSomething<string>("foo", "bar");
À ce stade, il n'y a qu'un seul membre de fonction applicable dans chaque appel.