5

Je viens de remarquer un comportement étrange avec une résolution de surcharge.Surcharge, inférence de type générique et le mot-clé 'params'

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, donc pour des raisons pratiques ajouter cette surcharge:

public static void DoSomething<T>(params T[] items) 
{ 
    // Whatever 

    // For debugging 
    Console.WriteLine("DoSomething<T>(params T[] items)"); 
} 

maintenant, je tente 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 m'attendrais à ce que la surcharge IEnumerable<T> soit appelée dans le cas d'un List<T>, parce que cela semble être un meilleur match (du moins pour moi).

Ce comportement est-il normal? Quelqu'un pourrait-il l'expliquer? Je n'ai pas pu trouver d'informations claires à ce sujet dans les documents MSDN ... Quelles sont les règles de résolution de surcharge impliquées ici?

Répondre

9

La section 7.4.3 de la spécification C# 3.0 est le bit pertinent ici. Fondamentalement, le tableau de paramètres est étendu, de sorte que vous comparez:

public static void DoSomething<T>(T item) 

et

public static void DoSomething<T>(IEnumerable<T> item) 

Le T pour le premier match est inférée être List<string> et T pour le deuxième match est déduit être string. Considérons maintenant les conversions impliquées pour l'argument au type de paramètre - dans le premier, List<string> à List<string>; dans la seconde c'est List<string> à IEnumerable<string>. La première conversion est meilleure que la seconde par les règles du 7.4.3.4.

Le bit contre-intuitif est l'inférence de type. Si vous prenez ce hors de l'équation, il fonctionnera comme prévu à:

var items = new List<string> { "foo", "bar" }; 
DoSomething<string>(items); 
DoSomething<string>("foo", "bar"); 

À ce moment-là, il n'y a qu'un seul membre de la fonction applicable dans chaque appel.

+0

Merci Jon, c'est exactement l'explication dont j'avais besoin. Existe-t-il une solution de rechange pour que cela fonctionne comme je le veux? Sinon, je suppose que je devrais utiliser un nom différent ... –

+0

Oui, j'ai remarqué que cela fonctionne si je spécifie explicitement le paramètre de type générique. Mais je perds le bénéfice de l'inférence de type ... –

+4

Je suggère d'utiliser un nom différent, juste pour plus de clarté - ou si vous allez l'utiliser avec plus d'un élément, vous pourriez faire '(T d'abord, params T [] autres) ' –

Questions connexes