c# overload-resolution

c# - ¿Por qué agregar un método agregaría una llamada ambigua, si no estaría involucrado en la ambigüedad?



overload-resolution (5)

¿Es esto un error?

Sí.

Felicidades, has 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. Le informé al equipo de prueba C # 5, y espero que podamos investigarlo y resolverlo antes del lanzamiento final. (Como siempre, sin promesas)

Un análisis correcto sigue. Los candidatos son:

0: C(params string[]) in its normal form 1: C(params string[]) in its expanded form 2: C<string>(string) 3: C(string, object)

El candidato cero obviamente no es aplicable porque la string no es convertible a la string[] . Eso deja tres.

De los tres, debemos determinar un mejor método único. Lo hacemos haciendo comparaciones por parejas de los tres candidatos restantes. Hay tres de esos pares. Todos ellos tienen listas de parámetros idénticos una vez que eliminamos los parámetros opcionales omitidos, lo que significa que tenemos que ir a la ronda avanzada de desempate descrita en la sección 7.5.3.2 de la especificación.

¿Cuál 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. Entonces 2 no puede ser el ganador.

¿Qué 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 lo tanto, 1 es peor que 3. Entonces 1 no puede ser el ganador.

¿Cuál 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. Entonces 2 no puede ser el ganador.

Para ser elegido entre un conjunto de múltiples candidatos aplicables, un candidato debe ser (1) invicto, (2) derrotar 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 supera 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 está equivocado, ya que se da cuenta correctamente de que está informando un mensaje de error extraño. Que el compilador está recibiendo el análisis de resolución de sobrecarga incorrectamente es un poco sorprendente. Que está recibiendo el mensaje de error incorrecto no es nada sorprendente; la heurística de error del "método ambiguo" selecciona básicamente dos métodos del conjunto candidato si no se puede determinar el mejor método. No es muy bueno para encontrar la ambigüedad "real", si es que hay una.

Uno podría razonablemente preguntar por qué es eso. Es bastante complicado encontrar dos métodos que son "ambiguamente ambiguos" porque la relación de "mejora" 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 "ambiguos".

Me gustaría mejorar esta heurística para Roslyn, pero es una prioridad baja.

(Ejercicio para el lector: "Idear un algoritmo de tiempo lineal para identificar al único mejor miembro de un conjunto de n elementos donde la relación de mejora es intransitiva" fue una de las preguntas que me formularon el día que entrevisté a este equipo. un algoritmo muy duro; pruébalo).

Una de las razones por las que insistimos en agregar argumentos opcionales a C # durante tanto tiempo fue el número 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 conexión para seguirlo, siéntase libre. Si solo quieres que lo traigamos a nuestra atención, considéralo hecho. Voy a seguir con las pruebas el próximo año.

Gracias por traer esto a mi atención. Disculpas por el error

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 el 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 de 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 un parámetro, y el segundo es un genérico.

¿Es esto un error? ¿Qué parte de la especificación dice que este debería ser el caso?


¿Qué parte de la especificación dice que este debería ser el caso?

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 tipo).

Tenga en cuenta especialmente la sección 7.5.3.2 (mejor miembro de la 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 amd 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 suficiente como para saber qué partes de la especificación controlan este comportamiento, y mucho menos para juzgar si es compatible.


  1. Si tú escribes

    var blah = new Overloaded(); blah.ComplexOverloadResolution("Which wins?");

    o simplemente escribe

    var blah = new Overloaded(); blah.ComplexOverloadResolution();

    terminará en el mismo método , en método

    public void ComplexOverloadResolution(params string[] something

    Esta es la causa de la palabra clave params que hace que coincida mejor con el caso cuando no se especificó ningún parámetro

  2. Si intentas agregar tu nuevo método como este

    public void ComplexOverloadResolution(string something) { Console.WriteLine("Added Later"); }

    Compilará perfectamente y llamará a este método, ya que es una combinación perfecta para su llamada con un parámetro de string . Mucho más fuerte que params string[] something .

  3. Usted declara el segundo método como lo hizo

    public void ComplexOverloadResolution(string something, object something=null);

    Compilador, salta en total confusión entre el primer método y este, acaba de agregar uno. Porque no sabe qué función debería tener ahora en su llamada

    var blah = new Overloaded(); blah.ComplexOverloadResolution("Which wins?");

    De hecho, si elimina el parámetro de cadena de la llamada, como un código siguiente, todo se compila correctamente y funciona como antes

    var blah = new Overloaded(); blah.ComplexOverloadResolution(); // will be ComplexOverloadResolution(params string[] something) function called here, like a best match.


Si elimina los params de su primer método, esto no sucederá. ComplexOverloadResolution(string) primer y tercer método tienen ambas llamadas válidas ComplexOverloadResolution(string) , pero si su primer método es public void ComplexOverloadResolution(string[] something) no habrá ambigüedad.

El suministro de valor para un object somethingElse = null parámetro 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 algunas cosas locas aquí. Si mueve su tercer método en código después del primero, se informará correctamente. Parece que está tomando las dos primeras sobrecargas y las informa, sin buscar la correcta.

''ConsoleApplication1.Program.ComplexOverloadResolution (params string [])'' y ''ConsoleApplication1.Program.ComplexOverloadResolution (string, object)''

Edit2: Nuevo hallazgo. Eliminar cualquier método de los tres anteriores no producirá ninguna ambigüedad entre los dos. Por lo tanto, parece que el conflicto solo aparece si hay tres métodos presentes, independientemente del orden.


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 :

public class Overloaded { public void ComplexOverloadResolution(params string[] somethings) { Console.WriteLine("Normal Winner"); } public void ComplexOverloadResolution<M>(M something) { Console.WriteLine("Confused"); } public void ComplexOverloadResolution(string something, object somethingElse = null) { Console.WriteLine("Added Later"); } } class Program { static void Main(string[] args) { Overloaded a = new Overloaded(); a.ComplexOverloadResolution(something:"asd"); } }