2009-06-07 12 views
6

¿Alguien sabe de una forma de interceptar las llamadas al método dynamic (particularmente aquellas que van a aumentar RuntimeBinderException s) con un RealProxy? Esperaba detectar la excepción e implementar el 'método que falta' además de eso, pero parece que se lanza antes de que el interceptor obtenga una inspección.Dificultades en el método en C# 4.0: dinámico vs RealProxy

Mi prueba sólo se parece a:

dynamic hello = MethodMissingInterceptor<DynamicObject>.Create(); 
Assert.AreEqual("World", hello.World()); 

Dónde World no se aplica realmente en DynamicObject. El interceptor es bastante sencillo - que estaba esperando para comprobar IMethodReturnMessage.Exception para RuntimeBinderException y hacia adelante a algo como:

public IMessage MethodMissing(IMethodCallMessage call) 
{ 
    return new ReturnMessage(call.MethodBase.Name, new object[0], 0, call.LogicalCallContext, call); 
} 

Por desgracia, todo lo que veo en mi interceptor son algunas llamadas a GetType, y no la inexistente World método .

En su defecto, ¿alguien sabe si hay una versión DynamicProxy ejecutándose felizmente en .NET 4.0 pero que podría haber solucionado el problema?

Respuesta

17

Comenzaré con la respuesta larga. Cada unen de una operación dinámica en C# hace aproximadamente estas tres cosas en este orden:

  1. Pregunte el objeto de obligar a sí mismo si implementa IDynamicMetaObjectProvider o es un objeto COM, y si eso no funciona, entonces ...
  2. Vincula la operación a una operación en un objeto plain-old-clr usando reflexión, y si eso falla, entonces ...
  3. Devuelve un DynamicMetaObject que representa una falla total de vinculación.

Estás viendo el GetType llama porque en el paso 2, el aglutinante # tiempo de ejecución C está reflejando sobre usted para tratar de averiguar si usted tiene un método "mundo" que es apropiado llamar, y esto está sucediendo porque la implementación IDynamicMetaObjectProvider de hello, si hay una, no podría encontrar nada especial que hacer.

Desafortunadamente para usted, cuando se lanza RuntimeBinderException, ya no somos vinculantes. La excepción proviene de la fase de ejecución de la operación dinámica, en respuesta al metaobjeto devuelto debido al paso 3. La única oportunidad para que usted lo capte es en el sitio real de la llamada.

De modo que esa estrategia no funcionará si desea implementar method_missing en C#. Aunque tienes algunas opciones.

Una opción fácil es implementar IDynamicMetaObjectProvider en su MethodMissingInterceptor, y diferir a la implementación de IDMOP del objeto envuelto. En caso de falla por parte del IDMOP interno, puede enlazar a lo que desee (tal vez una llamada a un delegado method_missing almacenado en el interceptor). La desventaja aquí es que esto solo funciona para objetos que se sabe que son objetos dinámicos, p. aquellos que implementan IDMOP para comenzar. Esto es porque básicamente se está insertando entre los pasos 1 y 2.

Otra alternativa que puedo pensar es implementar IDynamicMetaObjectProvider, y en él, responder positivamente a cada enlace, devolver una llamada a un método que (a) produce el mismo código que el compilador C# habría producido para vincular en primer lugar, y (b) atrapa RuntimeBinderException para llamar a un método method_missing.La desventaja aquí es que sería bastante complicado: tendría que generar tipos de delegado arbitrarios y el IL que los utiliza, contra los tipos públicos en el ensamblado de cuaderno de ejecución de C# que, francamente, no están destinados al consumo público. Pero al menos tendrías el método perdido contra todas las operaciones.

Estoy seguro de que hay otras estrategias en las que no he pensado, como las que parece dar a entender sobre el uso de proxys remotos. Aunque no puedo imaginar cómo se ven y no puedo decir si tendrían éxito.

La clave del problema aquí es que C# 4.0 no tiene un diseño que anticipe su deseo de hacer esto. Específicamente, no puede insertarse fácilmente entre los pasos 2 y 3. Eso me lleva a la respuesta corta, lo cual es lamentable, C# 4.0 no tiene method_missing.

+0

Gracias por la excelente explicación, Chris - Acabo de empezar a revisar tu serie de publicaciones "dinámicas" de C# en tu blog. :) Para mis propósitos, su primera solución parece que debería funcionar. Solo quiero hacer estas llamadas para objetos de estilo constructor y para cierta fluidez adicional en las API de prueba, no necesito atraparlos en objetos arbitrarios. – Thom

+0

@Chris: tenga en cuenta también la pregunta anterior "por las dudas"; en realidad, me gustaría saberlo ;-p –

+0

¿Hay alguna forma de consultar el objeto dinámico para la presencia de un miembro sin llamarlo realmente? –

Cuestiones relacionadas