Resolución de sobrecarga peculiar con while (true)


Estaba implementando sobrecargas sync / async cuando me encontré con esta situación peculiar:

Cuando tengo una expresión lambda regular sin parámetros o un valor de retorno va a la sobrecarga Run con el parámetro Action, que es predecible. Pero cuando ese lambda tiene un while (true) en él va a la sobrecarga con el parámetro Func.

public void Test()
{
    Run(() => { var name = "bar"; });
    Run(() => { while (true) ; });
}

void Run(Action action)
{
    Console.WriteLine("action");
}

void Run(Func<Task> func) // Same behavior with Func<T> of any type. 
{
    Console.WriteLine("func");
}

Salida:

Acción
func

Entonces, ¿cómo puede ser eso? ¿Hay alguna razón para ello?

Author: svick, 2014-06-20

1 answers

Así que para empezar, la primera expresión solo puede llamar a la primera sobrecarga. No es una expresión válida para un Func<Task> porque hay una ruta de código que devuelve un valor no válido (void en lugar de Task).

() => while(true) es en realidad un método válido para cualquiera de las firmas. (Junto con implementaciones como () => throw new Expression(); son cuerpos válidos de métodos que devuelven cualquier tipo posible, incluyendo void, un punto interesante de trivia, y por qué los métodos generados automáticamente desde un IDE típicamente solo lanza una excepción; se compilará independientemente de la firma del método.) Un método que hace bucles infinitamente es un método en el que no hay rutas de código que no devuelvan el valor correcto (y eso es cierto si el" valor correcto " es void, Task, o literalmente cualquier otra cosa). Esto es por supuesto porque nunca devuelve un valor, y lo hace de una manera que el compilador puede probar. (Si lo hizo de una manera que el compilador no pudo probar, ya que no ha resuelto el problema de detención después de todo, entonces estaríamos en el mismo barco que A.)

Entonces, para nuestro bucle infinito, que es mejor, dado que ambas sobrecargas son aplicables. Esto nos lleva a nuestra sección de mejora de las especificaciones de C#.

Si vamos a la sección 7.4.3.3, viñeta 4, vemos: {[12]]}

Si E es una función anónima, T1 y T2 son tipos de delegados o tipos de árbol de expresiones con listas de parámetros idénticas, y existe un tipo de retorno inferido X para E en el contexto de esa lista de parámetros (§7.4.2.11):

[...]

Si T1 tiene un tipo de retorno Y, y T2 es void returning, entonces C1 es la mejor conversión.

Así que al convertir desde un delegado anónimo, que es lo que estamos haciendo, preferirá la conversión que devuelve un valor en lugar de uno que es void, por lo que elige Func<Task>.

 26
Author: Servy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-19 21:37:00