5

Acabo de notar un comportamiento extraño con resolución de sobrecarga.Sobrecarga, inferencia de tipo genérico y la palabra clave 'params'

Supongamos que tengo el siguiente método:

public static void DoSomething<T>(IEnumerable<T> items) 
{ 
    // Whatever 

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

Ahora, sé que este método a menudo se llamará con un pequeño número de argumentos explícitos, por lo que por comodidad añado esta sobrecarga:

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

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

ahora trato de llamar a estos métodos:

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

Pero en ambos casos, el se llama sobrecarga con params. Hubiera esperado que se llamara la sobrecarga IEnumerable<T> en el caso de un List<T>, porque parece una mejor coincidencia (al menos para mí).

¿Este comportamiento es normal? ¿Alguien podría explicarlo? No pude encontrar ninguna información clara sobre eso en los documentos de MSDN ... ¿Cuáles son las reglas de resolución de sobrecarga involucradas aquí?

Respuesta

9

La sección 7.4.3 de la especificación C# 3.0 es el bit relevante aquí. Básicamente, la matriz de parámetros se expande, por lo que está comparando:

public static void DoSomething<T>(T item) 

y

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

El T para el primer partido se infiere que ser List<string> y la T para el segundo partido se infiere que string.

Ahora considere las conversiones involucradas para el argumento del tipo de parámetro - en el primero es List<string> a List<string>; en el segundo es List<string> a IEnumerable<string>. La primera conversión es mejor que la segunda según las reglas en 7.4.3.4.

El bit contraintuitivo es la inferencia de tipo. Si se toma que fuera de la ecuación, que funcionará como esperas que:

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

En ese momento, sólo hay un miembro de la función aplicable en cada llamada.

+0

Gracias Jon, esa es exactamente la explicación que necesitaba. ¿Hay algún tipo de solución para que funcione como yo quiero? Si no, supongo que tendré que usar un nombre diferente ... –

+0

Sí, me di cuenta de que funciona si especifico explícitamente el parámetro de tipo genérico. Pero pierdo el beneficio de la inferencia de tipo ... –

+4

Sugeriría usar un nombre diferente, solo por claridad, o si solo vas a usarlo con más de un elemento, podrías hacer '(T primero, params T [] otros) ' –

Cuestiones relacionadas