ACTUALIZACIÓN: Esta pregunta fue the subject of my blog on the 22nd of October, 2012. Gracias por la gran pregunta!
Por qué no puede la figura compilador a cabo el tipo de tipo de compilación de M(dynamic_expression)
si sólo hay una sobrecarga de la M o la totalidad de las sobrecargas de M tienen el mismo tipo de retorno?
El compilador puede averiguar el tipo de tiempo de compilación; el tipo de tiempo de compilación es dinámico, y el compilador se da cuenta de eso con éxito.
Creo que la pregunta que la intención de hacer es:
¿Por qué es el tipo de tiempo de compilación de M(dynamic_expression)
siempre dinámica, incluso en el caso raro y poco probable de que usted está haciendo una llamada dinámica completamente innecesario un método M que siempre se elegirá independientemente del tipo de argumento?
Cuando formula la pregunta de esa manera, se responde a sí misma.:-)
Razón uno:
Los casos se imagina son raros; para que el compilador pueda realizar el tipo de inferencia que describe, se debe conocer suficiente información para que el compilador pueda hacer casi un análisis de tipo estático completo de la expresión. Pero si estás en ese escenario, ¿por qué estás usando la dinámica en primer lugar? Se podría hacer mucho mejor decir simplemente:
object d = whatever;
Foo foo = new Foo();
int x = (d is string) ? foo.M((string)d) : foo((int)d);
Obviamente, si sólo hay una sobrecarga de M, entonces es aún más fácil: convertir el objeto al tipo deseado. Si falla en tiempo de ejecución porque el lanzamiento fue malo, ¡bueno, la dinámica también habría fallado!
Simplemente no hay necesidad de dinámica en el primer lugar en este tipo de escenarios, ¿por qué habríamos de hacer un montón de caras y difíciles de trabajo inferencia de tipos en el compilador para permitir un escenario que no queremos que el uso dinámico para en primer lugar?
Razón dos:
supongamos que sí dijo que la resolución de sobrecarga tiene reglas muy especiales si el grupo método se conoce de forma estática para contener un método. Estupendo. Ahora acabamos de agregar un nuevo tipo de fragilidad al lenguaje. Ahora, agregar una nueva sobrecarga cambia el tipo de devolución de una llamada a un tipo completamente diferente, un tipo que no solo causa semántica dinámica, sino también tipos de valores de cuadros. ¡Pero espera, se pone peor!
// Foo corporation:
class B
{
}
// Bar corporation:
class D : B
{
public int M(int x) { return x; }
}
// Baz corporation:
dynamic dyn = whatever;
D d = new D();
var q = d.M(dyn);
Supongamos que implementamos su función requiest e inferimos que q es int, por su lógica. Ahora corporación Foo añade:
class B
{
public string M(string x) { return x; }
}
Y de repente cuando corporación Baz vuelve a compilar su código, de repente, el tipo de q se convierte en voz baja dinámica, porque no sabemos en tiempo de compilación que dyn no es una cadena. ¡Eso es bizarre y cambio inesperado en el análisis estático! ¿Por qué un tercero agregar un nuevo método a una clase base hace que el tipo de una variable local cambie en un método completamente diferente en una clase completamente diferente que se escribe en una compañía diferente, una compañía que ni siquiera usa B directamente, pero solo a través de D?
Esta es una nueva forma del problema Clase de base quebradiza, y tratamos de minimizar los problemas de clase de base quebradiza en C#.
O, ¿y si en lugar de Foo Corp dijo:
class B
{
protected string M(string x) { return x; }
}
Ahora, por su lógica,
var q = d.M(dyn);
da q el tipo int cuando el código anterior es fuera de un tipo que hereda de D, pero
var q = this.M(dyn);
da el tipo de q como dinámico cuando dentro de un tipo que hereda de D! Como desarrollador, me parece bastante sorprendente.
Razón Tres:
hay demasiada inteligencia en C# ya. Nuestro objetivo no es construir un motor lógico que pueda resolver todas las restricciones de tipo posibles en todos los valores posibles dado un programa en particular. Preferimos tener reglas generales, comprensibles y comprensibles que se puedan anotar fácilmente e implementar sin errores. La especificación ya tiene ochocientas páginas y escribir un compilador sin errores es increíblemente difícil. No lo hagamos más difícil. Por no mencionar el costo de prueba todos esos casos locos.
Razón cuatro:
otra parte: el idioma que ofrece muchas oportunidades para servirse del analizador de tipo estático. Si está utilizando la dinámica, está específicamente solicitando que ese analizador difiera su acción hasta el tiempo de ejecución. No debería ser una sorpresa que el uso de la característica "dejar de hacer análisis de tipo estático en tiempo de compilación" haga que el análisis de tipo estático no funcione muy bien en tiempo de compilación.
Cualquiera que involucre dinámica es, por la especificación, dinámico ** completamente ** hasta que se convierte en algo más. La dinámica es infecciosa y se expande para hacer que todo lo que * utiliza * dinámico - ** se vuelva ** dinámico. –
Sí, lo sé. La pregunta es * por qué * está la especificación escrita de esta manera, incluso cuando solo hay un tipo posible para la expresión. Aquí, estamos viendo el ejemplo de una expresión de invocación. – jason
No creo que este sea el motivo, de hecho, es posible que nunca se haya tenido en cuenta durante el diseño, pero mantener el resultado "dinámico" evita el desempaquetado y el nuevo compartimiento si el resultado es un tipo de valor y el resto del método continúa que resultan en otras llamadas al método 'dinámico'. – hvd