2011-10-26 13 views
5

El código siguiente muestra mi pregunta:conversión explícita del valor de retorno método dinámico dentro de un método no permite un método de extensión que se llamará

public class DynamicExample 
{ 
    public void DoSomething() 
    { 
     var x = new ExpandoObject(); 
     dynamic d = x; 
     d.GetString = (Func<string>)(() => "Some Value"); 

     d.GetString().SomeStringExtension(); // Doesn't work - expected 
     ((string)d.GetString()).SomeStringExtension(); // Works - expected 
     Build(d).SomeStringExtension(); // Doesn't work - unexpected? 
    } 

    private static string Build(dynamic d) 
    { 
     return (string)d.GetString(); 
    } 
} 

public static class StringExtensions 
{ 
    public static int SomeStringExtension(this string s) 
    { 
     return s.Length; 
    } 
} 

La pregunta es, ¿por qué hay una diferencia entre el compilador ¿Lanzando el tipo en línea a la llamada del método de extensión y moviendo ese lanzamiento en un método separado?

+1

+1 Como una pregunta adicional: ¿por qué en 'var s = Build (d);' the 'var' es dinámico? (Añadiré que esta es, probablemente, la verdadera pregunta. Si el resultado de 'Build (d)' es implícitamente dinámico, entonces la resolución de 'SomeStringExtension' no se puede hacer en tiempo de ejecución) – xanatos

+0

No funciona porque 'dynamic' le está diciendo al compilador que deje de hacer análisis de tipo en la variable. ¿Cómo espera que coincida con los métodos de extensión sin esa información? El molde explicado funciona porque tu conversión explícita a un tipo de tiempo de compilación conocido. – asawyer

+0

De acuerdo. Sospecho que el compilador está decidiendo que todo el método Build (dynamic d) debe estar sujeto a enlaces dinámicos, pero no entiendo el motivo. (Se va a editar la pregunta para deshacerse de los múltiples métodos de compilación, de forma tal que los comentarios sean válidos) –

Respuesta

5

Build(d) sigue siendo una expresión dinámica - el tipo en tiempo de compilación del método es dynamic a pesar de que puede ver exactamente lo que está pasando. Eso significa que los métodos de extensión no funcionarán.

Básicamente el compilador sigue razonablemente sencillas reglas para determinar cuál es el tipo de una expresión es, y casi cualquier expresión que implique dynamic termina siendo considerada como una expresión dinámica. Las excepciones a esto son:

  • d is SomeType (siempre considerado como bool)
  • yesos, tanto directa como mediante as

Eso es por lo que yo puedo recordar, a pesar de que podría ser confundido ...

Ahora el idioma podría haber sido diseñado de tal manera que este caso sería estáticamente resuelva la llamada al Build como la única razonable; después de todo, es imposible que d sea de cualquier tipo de que cambiaría el método que se llama, pero especificar las reglas exactas para eso haría que las especificaciones del lenguaje (y el compilador) significativamente más complicado con relativamente poca ganancia.

+0

Así que puedes usar sobrecargas de métodos (si hay sobrecargas de compilación en el presente, se elegiría la correcta en tiempo de ejecución) pero pierdes el uso de métodos de extensión, ¿no? – xanatos

+0

@xanatos: Sin un yeso u otra conversión a un tipo "conocido", sí. –

+0

¡Sabía que Jon no estaría lejos de este tipo de preguntas! Estoy aceptando esta respuesta porque explica que el reparto se trata de manera diferente y, por lo tanto, explica por qué hay una diferencia entre los dos últimos intentos de llamar al método de extensión. –

5

Si pasa el mouse sobre Build(d) en VS2010, verá que toda la expresión se considera dinámica y se resolverá en tiempo de ejecución. Como tal, no se puede vincular al método de extensión (que de otro modo ocurriría en tiempo de compilación).

El motivo por el que toda la expresión es dynamic es que sin conocer el tipo de argumento en tiempo de compilación, resolución de sobrecarga no puede ser no se realiza, por lo que el tipo de devolución del método no puede ser tampoco se conoce.

+0

En este caso, la resolución de sobrecarga * podría * realizarse, ya que el compilador * podría * observar que * es * un método accesible en esta clase, y es el * único método accesible, y siempre será válido para el tipo de argumento . Pero este es un caso relativamente raro. –

+0

@JonSkeet Por supuesto. Como dijiste, sin embargo, no :) – dlev

+0

Es por eso que creo que usaría la palabra "no es" en lugar de "no puedo" aquí :) –

Cuestiones relacionadas