Tengo esta clase
public class Overloaded
{
public void ComplexOverloadResolution(params string[] something)
{
Console.WriteLine("Normal Winner");
}
public void ComplexOverloadResolution<M>(M something)
{
Console.WriteLine("Confused");
}
}
Si lo llamo así:
var blah = new Overloaded();
blah.ComplexOverloadResolution("Which wins?");
Escribe Normal Winner
en la consola.
Pero, si agrego otro método:
public void ComplexOverloadResolution(string something, object somethingElse = null)
{
Console.WriteLine("Added Later");
}
Obtuve el siguiente error:
La llamada es ambigua entre los siguientes métodos o propiedades:> '
Overloaded.ComplexOverloadResolution(params string[])
' y 'Overloaded.ComplexOverloadResolution<string>(string)
'
Puedo entender que agregar un método podría introducir una ambigüedad en la llamada, ¡pero es una ambigüedad entre los dos métodos que ya existían (params string[])
y <string>(string)
! Claramente, ninguno de los dos métodos involucrados en la ambigüedad es el método recién agregado, porque el primero es params y el segundo es genérico.
¿Es esto un error? ¿Qué parte de la especificación dice que este debería ser el caso?
c#
overload-resolution
McKay
fuente
fuente
'Overloaded.ComplexOverloadResolution(string)'
refiera al<string>(string)
método; Creo que se refiere al(string, object)
método sin objeto proporcionado.Respuestas:
Si.
Felicitaciones, ha encontrado un error en la resolución de sobrecarga. El error se reproduce en C # 4 y 5; no se reproduce en la versión "Roslyn" del analizador semántico. He informado al equipo de prueba de C # 5 y espero que podamos investigar y resolver esto antes del lanzamiento final. (Como siempre, sin promesas).
Sigue un análisis correcto. Los candidatos son:
El candidato cero es obviamente inaplicable porque
string
no es convertible astring[]
. Eso deja tres.De los tres, debemos determinar el mejor método único. Lo hacemos haciendo comparaciones por pares de los tres candidatos restantes. Hay tres pares de este tipo. Todos ellos tienen listas de parámetros idénticas una vez que eliminamos los parámetros opcionales omitidos, lo que significa que tenemos que pasar a la ronda avanzada de desempate descrita en la sección 7.5.3.2 de la especificación.
¿Qué es mejor, 1 o 2? El desempate relevante es que un método genérico siempre es peor que un método no genérico. 2 es peor que 1. Así que 2 no puede ser el ganador.
¿Cuál es mejor, 1 o 3? El desempate relevante es: un método aplicable solo en su forma expandida es siempre peor que un método aplicable en su forma normal. Por tanto, 1 es peor que 3. Por tanto, 1 no puede ser el ganador.
¿Qué es mejor, 2 o 3? El desempate relevante es que un método genérico siempre es peor que un método no genérico. 2 es peor que 3. Así que 2 no puede ser el ganador.
Para ser elegido de un conjunto de múltiples candidatos aplicables, un candidato debe estar (1) invicto, (2) vencer al menos a otro candidato y (3) ser el candidato único que tiene las dos primeras propiedades. El candidato tres no es derrotado por ningún otro candidato y gana al menos a otro candidato; es el único candidato con esta propiedad. Por lo tanto, el candidato tres es el mejor candidato único . Debería ganar.
El compilador de C # 4 no solo se equivoca, sino que también notó correctamente que está informando un extraño mensaje de error. Que el compilador esté haciendo mal el análisis de resolución de sobrecarga es un poco sorprendente. Que el mensaje de error sea incorrecto no es ninguna sorpresa; la heurística de error del "método ambiguo" básicamente elige dos métodos del conjunto de candidatos si no se puede determinar el mejor método. No es muy bueno para encontrar la ambigüedad "real", si es que la hay.
Uno podría preguntarse razonablemente por qué es así. Es bastante complicado encontrar dos métodos que sean "inequívocamente ambiguos" porque la relación de "mejoría" es intransitiva . Es posible llegar a situaciones en las que el candidato 1 es mejor que 2, 2 es mejor que 3 y 3 es mejor que 1. En tales situaciones, no podemos hacer nada mejor que elegir dos de ellos como "los ambiguos".
Me gustaría mejorar esta heurística para Roslyn pero es una prioridad baja.
(Ejercicio para el lector: "Diseñar un algoritmo de tiempo lineal para identificar el mejor miembro único de un conjunto de n elementos donde la relación de mejora es intransitiva" fue una de las preguntas que me hicieron el día que me entrevisté para este equipo. No es un algoritmo muy difícil; pruébalo.)
Una de las razones por las que rechazamos la adición de argumentos opcionales a C # durante tanto tiempo fue la cantidad de situaciones ambiguas complejas que introduce en el algoritmo de resolución de sobrecarga; aparentemente no lo hicimos bien.
Si desea ingresar un problema de Connect para rastrearlo, no dude. Si solo quiere que nos lo notifique, considérelo hecho. Seguiré con las pruebas el próximo año.
Gracias por informarme sobre esto. Disculpas por el error.
fuente
Sección 7.5.3 (resolución de sobrecarga), junto con las secciones 7.4 (búsqueda de miembros) y 7.5.2 (inferencia de tipos).
Nótese especialmente la sección 7.5.3.2 (mejor miembro de función), que dice en parte "los parámetros opcionales sin argumentos correspondientes se eliminan de la lista de parámetros" y "Si M (p) es un método no genérico y M (q) es un método genérico, entonces M (p) es mejor que M (q) ".Sin embargo, no entiendo estas partes de la especificación lo suficientemente a fondo como para saber qué partes de la especificación controlan este comportamiento, y mucho menos para juzgar si es compatible.
fuente
puede evitar esta ambigüedad cambiando el nombre del primer parámetro en algunos métodos y especificando el parámetro que desea asignar
Me gusta esto :
fuente
Si elimina el
params
de su primer método, esto no sucederá.ComplexOverloadResolution(string)
Su primer y tercer método tienen llamadas válidas , pero si su primer método es,public void ComplexOverloadResolution(string[] something)
no habrá ambigüedad.Proporcionar valor para un parámetro lo
object somethingElse = null
convierte en un parámetro opcional y, por lo tanto, no tiene que especificarse al llamar a esa sobrecarga.Editar: El compilador está haciendo cosas locas aquí. Si mueve su tercer método en código después del primero, informará correctamente. Así que parece que está tomando las dos primeras sobrecargas y las reporta, sin verificar cuál es la correcta.
Edit2: Nuevo hallazgo. Eliminar cualquier método de los tres anteriores no producirá ambigüedad entre los dos. Entonces, parece que el conflicto solo aparece si hay tres métodos presentes, independientemente del orden.
fuente
Si tú escribes
o simplemente escribe
terminará en el mismo método , en el método
Esta es la
params
palabra clave de la causa que hace que coincida mejor también para el caso en que no se haya especificado ningún parámetroSi intenta agregar su nuevo método como este
Compilará y llamará perfectamente a este método, ya que es una combinación perfecta para su llamada con un
string
parámetro. Mucho más fuerte entoncesparams string[] something
.Declaras el segundo método como lo hiciste
Compilador, salta en total confusión entre el primer método y este, que acaba de agregar uno. Porque no sabe qué función debería tener ahora en tu llamada
De hecho, si elimina el parámetro de cadena de la llamada, como el siguiente código, todo se compila correctamente y funciona como antes
fuente
state
3 funciones, no una o dos, para ser precisos. De hecho, basta con comentar alguno de ellos para resolver un problema. La mejor coincidencia entre las funciones disponibles es aquella conparams
, la segunda es aquella congenerics
parámetro, cuando agregamos la tercera crea una confusión en unaset
de las funciones. Creo que lo más probable es que no sea un mensaje de error claro producido por el compilador.