visual valid remarks net method example documentacion description comment comentarios c# overloading params roslyn

c# - valid - params sobrecarga aparente ambigüedad-todavía compila y funciona?



remarks c# (3)

Acabamos de encontrar estos en nuestro código:

public static class ObjectContextExtensions { public static T Find<T>(this ObjectSet<T> set, int id, params Expression<Func<T, object>>[] includes) where T : class { ... } public static T Find<T>(this ObjectSet<T> set, int id, params string[] includes) where T : class { ... } }

Como puede ver, estos tienen la misma firma, excepto los params .

Y están siendo utilizados de varias maneras, una de ellas:

DBContext.Users.Find(userid.Value); //userid being an int? (Nullable<int>)

lo cual, extrañamente para mí, resuelve la primera sobrecarga.

P1: ¿Por qué esto no produce un error de compilación?

P2: ¿Por qué el compilador de C # resuelve la llamada anterior al primer método?

Edición : Solo para aclarar, esto es C # 4.0, .Net 4.0, Visual Studio 2010.


Esta no es una respuesta completa, ya que explica las diferencias pero no por qué. Realmente necesita una referencia de especificación para estar completo. Sin embargo, no quería que la investigación que hice se perdiera en los comentarios, así que la publico como respuesta.

La diferencia entre las dos sobrecargas radica en el hecho de que los parámetros para uno son genéricos y el otro no. El compilador parece decidir que el tipo genérico es más cercano que el no genérico.

Es decir, si el tipo de Expression<...> se cambió a un int el compilador se quejaría de la ambigüedad. Similar si ambos tipos son genéricos, entonces se quejan de ambigüedad.

El siguiente fragmento de código mostrará este comportamiento de manera más simple:

void Main() { TestMethod(); } public void TestMethod(params string[] args) { Console.WriteLine("NonGeneric"); } public void TestMethod(params List<string>[] args) { Console.WriteLine("Generic"); }

Esto imprimirá "Genérico".


Esto es claramente un error en la resolución de sobrecarga.

Se reproduce en C # 5 y C # 3 pero no en Roslyn; No recuerdo si decidimos tomar deliberadamente el cambio de ruptura o si esto es un accidente. (No tengo C # 4 en mi máquina en este momento, pero si se reprosca en 3 y 5, también lo será en 4 también).

Lo he llamado la atención de mis antiguos colegas en el equipo de Roslyn. Si me contactan con algo interesante, actualizaré esta respuesta.

Como ya no tengo acceso al código fuente de C # 3/4/5, no puedo decir cuál es la causa del error. Considere reportarlo en connect.microsoft.com.

Aquí hay un repro muy simplificado:

class P { static void M(params System.Collections.Generic.List<string>[] p) {} static void M(params int[] p) {} static void Main() { M(); } }

Parece tener algo que ver con la genéricoidad del tipo de elemento. Curiosamente, como Chris señala en su respuesta, ¡el compilador elige el más genérico! Habría esperado que el error fuera a la inversa, y elegir el menos genérico.

El error es, por cierto, probablemente mi culpa, ya que hice una buena cantidad del trabajo en el algoritmo de resolución de sobrecarga en C # 3. Disculpas por el error.

ACTUALIZAR

Mis espías en el equipo de Roslyn me dicen que este es un error conocido de larga data en la resolución de sobrecargas. Hubo una regla de desempate implementada que nunca fue documentada o justificada y dijo que el tipo con la aridad genérica más grande era el mejor. Esta es una regla extraña sin justificación, pero nunca se ha eliminado del producto. El equipo de Roslyn decidió hace un tiempo tomar el cambio de última hora y corregir la resolución de sobrecarga para que produzca un error en este caso. (No recuerdo esa decisión, pero tomamos muchas decisiones sobre este tipo de cosas)


En IDEONE el compilador produce con éxito un error . Y debería ser un error, si analiza el algoritmo de resolución paso a paso:

1) Se construye el conjunto de métodos candidatos para la invocación del método. Comenzando con el conjunto de métodos asociados con M, que se encontraron en una búsqueda de miembros anterior [...] La reducción del conjunto consiste en aplicar las siguientes reglas a cada método TN en el conjunto, donde T es el tipo en el que el método N se declara:

Para simplificar, podemos deducir aquí que el conjunto de métodos aquí contiene ambos métodos.

Luego procede la reducción:

2) Si N no es aplicable con respecto a A ( Sección 7.4.2.1 ), entonces N se elimina del conjunto.

Ambos métodos son aplicables con respecto a las reglas de miembro de función Aplicable en su forma expandida :

La forma expandida se construye reemplazando la matriz de parámetros en la declaración del miembro de la función con cero o más parámetros de valor del tipo de elemento de la matriz de parámetros, de modo que el número de argumentos en la lista de argumentos A coincida con el número total de parámetros. Si A tiene menos argumentos que el número de parámetros fijos en la declaración del miembro de la función, la forma expandida del miembro de la función no se puede construir y, por lo tanto, no es aplicable.

Esta regla deja ambos métodos en el conjunto de reducción.

Los experimentos (que cambian el tipo de parámetro id para float en uno o ambos métodos) confirman que ambas funciones permanecen en el conjunto candidato, y son discriminadas por las reglas de comparación de conversión implícitas .

Esto sugiere que el algoritmo anterior funciona bien en términos de crear el conjunto candidato y no depende de algún orden interno de los métodos. Ya que lo único que discrimina a los métodos son las reglas de resolución de sobrecarga, esto parece ser un error , porque:

el mejor miembro de la función es el miembro de una función que es mejor que todos los demás miembros de la función con respecto a la lista de argumentos dada, siempre que cada miembro de la función se compare con todos los demás miembros de la función utilizando las reglas en la Sección 7.4.2.2 .

y claramente ninguno de estos métodos es mejor que otro, porque aquí no existen conversiones implícitas.