2011-09-12 17 views
12

Considere el siguiente programa de ejemplo: trabaja¿Debería una expresión de tipo 'dinámico' comportarse de la misma manera en tiempo de ejecución que una no dinámica del mismo tiempo de tipo de ejecución?

using System; 
public delegate string MyDelegateType(int integer); 

partial class Program 
{ 
    static string MyMethod(int integer) { return integer.ToString(); } 

    static void Main() 
    { 
     Func<int, string> func = MyMethod; 

     // Scenario 1: works 
     var newDelegate1 = new MyDelegateType(func); 
     newDelegate1(47); 

     // Scenario 2: doesn’t work 
     dynamic dyn = func; 
     var newDelegate2 = new MyDelegateType(dyn); 
     newDelegate2(47); 
    } 
} 

El primero de ellos como era de esperar - la conversión a MyDelegateType tiene éxito. El segundo, sin embargo, arroja una RuntimeBinderException con el mensaje de error:

No se puede convertir implícitamente el tipo 'System.Func < int, cadena >' a 'MyDelegateType'

¿Hay algo en el C# especificación que permite este comportamiento, o es un error en el compilador C# de Microsoft?

Respuesta

16

Buena captura Timwi.

Nuestro soporte para grupos de métodos dinámicos es débil. Por ejemplo, considere este caso más simple:

class C 
{ 
    public void M() {} 
} 

class P 
{ 
    static void Main() 
    { 
     dynamic d = new C(); 
     C c = new C(); 
     Action a1 = c.M; // works 
     Action a2 = d.M; // fails at runtime 

El d.M se interpreta como una obtención de propiedad (o el acceso de campo) por el tiempo de ejecución dinámica, y cuando se resuelve como un grupo método, se produce un error en tiempo de ejecución.

Lo mismo está sucediendo en su caso, es un poco más oscuro. Cuando dice MyDelegate x = new MyDelegate(someOtherDelegate); que el compilador trata como si dijera MyDelegate x = someOtherDelegate.Invoke;. La pieza dinámica de tiempo de ejecución no sabe hacer esa transformación, y aunque lo hiciera, no podría manejar la resolución del grupo de métodos que es el resultado de la parte .Invoke de la expresión.

¿Hay algo en la especificación C# que permita este comportamiento, o es esto un error en el compilador C# de Microsoft?

La especificación no indica que esto debería ser un error de tiempo de ejecución, y implica que debe manejarse correctamente en el tiempo de ejecución; claramente la implementación no lo hace. Aunque es una falla de la implementación, no lo llamaría un "error" porque deliberadamente hicimos el comportamiento que descubrió. No teníamos los recursos para hacer que este tipo de expresiones funcionaran exactamente, así que las dejamos como errores. Si alguna vez obtenemos una buena forma de representar grupos de métodos en el tiempo de ejecución dinámico, podemos implementarlo.

De manera similar, no hay manera en el código dinámico para representar la noción de "esta cosa dinámica es una expresión lambda donde los tipos de los parámetros se determinan en tiempo de ejecución". Si tenemos una buena manera de representarlos en el futuro, podríamos hacer el trabajo.

Sam habló un poco sobre esto en 2008; véase su artículo en él:

http://blogs.msdn.com/b/samng/archive/2008/11/02/dynamic-in-c-ii-basics.aspx

+0

La raíz del problema es que los grupos de métodos están sin tipo, ¿no? Introducir un CaseDelegate, que se puede invocar con diferentes sobrecargas, puede ser interesante, aunque requeriría muchos tipos diferentes para cada posible combinación de sobrecargas. – configurator

2

me he encontrado a esta limitación también. Aunque no pude responder por qué mejor que Eric Lippert, hay una solución directa.

var newDelegate2 = new MyDelegateType(x => dyn(x)); 

Obtiene implícitamente la firma estática del delegado y la invocación dinámica funciona sin más información.Esto funciona para los delegados y, como bonificación, objetos dinámicos llamables.

+0

Sí, pero la desventaja de esto es que la invocación de delegado pasa por el DLR * cada vez * que lo llame. Quería que fuera dinámico solo una vez, cuando el delegado estaba configurado; después de eso, debería ser una simple invocación de un 'MyDelegateType'. – Timwi

+0

Cierto, pero * cada vez * no incluye una compilación de expresiones, es solo la primera vez, así que cuando realicé algunas ejecuciones con un millón de llamadas de delegados, promedió 20 milisegundos más lentamente utilizando la versión dlr y el tiempo máximo de ejecución de una millones de llamadas fueron 130 milisegundos. Sin embargo, si sabes que tu variable 'dyn' es' Delegado', siempre puedes usar el reflejo, pagas mucho más por la creación, pero como eso no es un punto de acceso típico, probablemente funcione en cuanto al rendimiento. '(MyDelegateType) Delegate.CreateDelegate (typeof (MyDelegateType), (object) dyn," Invoke ")' – jbtule

+0

Gracias por su respuesta. Sin embargo, el rendimiento no es el único problema con esto. Mi problema principal es que cualquier cosa que use 'dinámico' puede arrojar muchas formas diferentes en tiempo de ejecución; en otras palabras, no es robusto. Si mi aplicación (que es un servidor) está configurada incorrectamente, debe arrojarse durante la inicialización, no repentinamente en el medio de la operación, ya que recibe una solicitud poco común que sucede desencadenando un caso de esquina. – Timwi

Cuestiones relacionadas