sobrecarga sirve que programacion polimorfico para orientada objetos metodos constructores clases c# c#-4.0 overload-resolution method-group

c# - sirve - sobrecarga de metodos c++



El argumento del grupo de métodos sobrecargado confunde la resolución de sobrecarga? (2)

Supongo que el compilador está confundido por Tuple.Create<char, int>(char, int) , pero su tipo de retorno es incorrecto: devuelve dos tuplas.

El tipo de devolución no forma parte de la firma del método, por lo que no se considera durante la resolución de sobrecarga; solo se verifica después de que se ha elegido una sobrecarga. Así que, por lo que el compilador sabe, Tuple.Create<char, int>(char, int) es un candidato válido, y no es ni mejor ni peor que Tuple.Create<char>(char) , por lo que el compilador no puede decidir.

La siguiente llamada al método Enumerable.Select sobrecargado:

var itemOnlyOneTuples = "test".Select<char, Tuple<char>>(Tuple.Create);

falla con un error de ambigüedad (se eliminaron los espacios de nombres para mayor claridad):

The call is ambiguous between the following methods or properties: ''Enumerable.Select<char,Tuple<char>> (IEnumerable<char>,Func<char,Tuple<char>>)'' and ''Enumerable.Select<char,Tuple<char>> (IEnumerable<char>, Func<char,int,Tuple<char>>)''

Ciertamente, puedo entender por qué no especificar explícitamente los argumentos tipo daría lugar a una ambigüedad (ambas sobrecargas se aplicarían), pero no veo una después de hacerlo.

Parece lo suficientemente claro para mí que la intención es llamar a la primera sobrecarga, resolviendo el argumento del grupo de Tuple.Create<char>(char) a Tuple.Create<char>(char) . La segunda sobrecarga no debería aplicarse porque ninguna de las sobrecargas Tuple.Create se puede convertir al tipo Func<char,int,Tuple<char>> esperado. Supongo que el compilador está confundido por Tuple.Create<char, int>(char, int) , pero su tipo de retorno es incorrecto: devuelve dos tuplas y, por lo tanto, no es convertible al tipo de Func correspondiente.

Por cierto, cualquiera de los siguientes hace feliz al compilador:

  1. Especificando un argumento de tipo para el argumento de grupo de Tuple.Create<char> : Tuple.Create<char> (¿Tal vez esto es realmente un problema de inferencia de tipo?).
  2. Convertir el argumento en una expresión lambda en lugar de un grupo de métodos: x => Tuple.Create(x) . (Juega bien con la inferencia de tipo en la llamada Select ).

Como era de esperar, intentar llamar a la otra sobrecarga de Select de esta manera también falla:

var itemIndexTwoTuples = "test".Select<char, Tuple<char, int>>(Tuple.Create);

¿Cuál es el problema exacto aquí?


Primero, observo que este es un duplicado de:

¿Por qué Func <T> es ambiguo con Func <IEnumerable <T >>?

¿Cuál es el problema exacto aquí?

La suposición de Thomas es esencialmente correcta. Aquí están los detalles exactos.

Vamos a ir paso por paso. Tenemos una invocación:

"test".Select<char, Tuple<char>>(Tuple.Create);

La resolución de sobrecarga debe determinar el significado de la llamada a Seleccionar. No hay un método "Seleccionar" en la cadena o cualquier clase base de cadena, por lo que este debe ser un método de extensión.

Hay una serie de posibles métodos de extensión para el conjunto candidato porque la cadena es convertible a IEnumerable<char> y presumiblemente hay un using System.Linq; allí en algún lado. Hay muchos métodos de extensión que coinciden con el patrón "Seleccionar, aridad genérica dos, toma un IEnumerable<char> como el primer argumento cuando se construye con los argumentos de tipo de método dados".

En particular, dos de los candidatos son:

Enumerable.Select<char,Tuple<char>>(IEnumerable<char>,Func<char,Tuple<char>>) Enumerable.Select<char,Tuple<char>>(IEnumerable<char>,Func<char,int,Tuple<char>>)

Ahora, la primera pregunta que enfrentamos es si los candidatos son aplicables . Es decir, ¿hay una conversión implícita de cada argumento proporcionado al tipo de parámetro formal correspondiente?

Una excelente pregunta. Claramente, el primer argumento será el "receptor", una cadena, y será implícitamente convertible a IEnumerable<char> . La pregunta ahora es si el segundo argumento, el grupo de métodos "Tuple.Create", es implícitamente convertible a los tipos de parámetros formales Func<char,Tuple<char>> y Func<char,int, Tuple<char>> .

¿Cuándo es un grupo de métodos convertible a un tipo de delegado determinado? Un grupo de métodos es convertible a un tipo de delegado cuando la resolución de sobrecarga hubiera tenido éxito dados argumentos del mismo tipo que los tipos de parámetros formales del delegado .

Es decir, M es convertible a Func<A, R> si la resolución de sobrecarga en una llamada de la forma M(someA) hubiera tenido éxito, dada una expresión ''algunaA'' de tipo ''A''.

¿Habría tenido éxito la resolución de sobrecarga en una llamada a Tuple.Create(someChar) ? Sí; la resolución de sobrecarga habría elegido Tuple.Create<char>(char) .

¿Habría tenido éxito la resolución de sobrecarga en una llamada a Tuple.Create(someChar, someInt) ? Sí, la resolución de sobrecarga habría elegido Tuple.Create<char,int>(char, int) .

Como en ambos casos la resolución de sobrecarga habría tenido éxito, el grupo de métodos es convertible a ambos tipos de delegados. El hecho de que el tipo de devolución de uno de los métodos no coincidiera con el tipo de devolución del delegado es irrelevante; la resolución de sobrecarga no funciona o falla según el análisis del tipo de devolución .

Se podría decir razonablemente que la convertibilidad de los grupos de métodos para delegar tipos debería tener éxito o fallar en función del análisis del tipo de devolución, pero no es así como se especifica el idioma; el idioma se especifica para usar la resolución de sobrecarga como la prueba para la conversión del grupo de métodos, y creo que es una opción razonable.

Por lo tanto, tenemos dos candidatos aplicables. ¿Hay alguna forma de que podamos decidir cuál es mejor que el otro? La especificación indica que la conversión al tipo más específico es mejor; si usted tiene

void M(string s) {} void M(object o) {} ... M(null);

luego, la resolución de sobrecarga elige la versión de cadena porque la cadena es más específica que el objeto. ¿Es uno de esos tipos de delegados más específico que el otro? No. Ninguno es más específico que el otro. (Esta es una simplificación de las reglas de conversión mejoradas, en realidad hay muchos desempates, pero ninguno se aplica aquí).

Por lo tanto, no hay ninguna base para preferir uno sobre el otro.

De nuevo, uno podría razonablemente decir que seguro, existe una base, a saber, que una de esas conversiones produciría un error de desajuste del tipo de devolución del delegado y una de ellas no. De nuevo, sin embargo, el lenguaje se especifica para razonar sobre la mejoría al considerar las relaciones entre los tipos de parámetros formales , y no sobre si la conversión que ha elegido eventualmente producirá un error.

Como no hay ninguna base sobre la cual preferir uno sobre el otro, este es un error de ambigüedad.

Es fácil construir errores de ambigüedad similares. Por ejemplo:

void M(Func<int, int> f){} void M(Expression<Func<int, int>> ex) {} ... M(x=>Q(++x));

Eso es ambiguo Aunque es ilegal tener un ++ dentro de un árbol de expresiones, la lógica de convertibilidad no considera si el cuerpo de un lambda tiene algo dentro de él que sería ilegal en un árbol de expresiones . La lógica de conversión simplemente se asegura de que los tipos revisen, y lo hacen. Dado eso, no hay ninguna razón para preferir una de las M sobre la otra, así que esto es una ambigüedad.

Usted nota que

"test".Select<char, Tuple<char>>(Tuple.Create<char>);

tiene éxito Ahora sabes por qué. La resolución de sobrecarga debe determinar si

Tuple.Create<char>(someChar)

o

Tuple.Create<char>(someChar, someInt)

Tendría éxito. Dado que el primero lo hace y el segundo no, el segundo candidato es inaplicable y eliminado, y por lo tanto no está cerca para volverse ambiguo.

También nota que

"test".Select<char, Tuple<char>>(x=>Tuple.Create(x));

es inequívoco. Las conversiones Lambda tienen en cuenta la compatibilidad del tipo de expresión devuelta con el tipo de devolución del delegado objetivo. Es desafortunado que los grupos de métodos y las expresiones lambda usen dos algoritmos sutilmente diferentes para determinar la convertibilidad, pero ahora estamos atascados. Recuerde, las conversiones de grupos de métodos han estado en el lenguaje mucho más tiempo que las conversiones de lambda; si se hubieran agregado al mismo tiempo, me imagino que sus reglas se habrían hecho consistentes.