2010-12-04 13 views
5

Considere el siguiente código trivial:¿Inyección de código de tiempo de ejecución utilizando DynamicMethod?

using System; 
class Test 
{ 
    delegate int FooDelegate(int i); 
    FooDelegate Foo = FooImplementation; 
    static int FooImplementation(int i) 
    { 
     return i + 1; 
    } 

    public static void Main() 
    { 
     Foo(1); 
    } 
} 

Lo que me gustaría hacer es inyectar algo de código de depuración en el delegado de Foo, lo que sería equivalente:

FooDelegate Foo = delegate(int i) 
{ 
    try 
    { 
     DebugPrologue(); 
     return FooImplementation(i); 
    } 
    finally 
    { 
     DebugEpilogue(); 
    } 
}; 

El giro es que debo estar capaz de hacer esto en runtime, por lo que los métodos de tiempo de compilación y posprocesamiento están fuera de la cuestión.

Mi enfoque inicial usó Delegate.Combine() para agregar los métodos de prólogo y epílogo al delegado de Foo. Por desgracia, esto no funcionará ya que genera valores de retorno.

Mi idea actual es utilizar System.Reflection.Emit y DynamicMethod como una posible solución. Por lo que puedo decir, necesito obtener MethodInfo for FooImplementation, obtener su MethodBody, convertir eso en un DynamicMethod e inyectar mi bloque try-finally en eso.

Lamentablemente, no tengo ni idea de cómo hacerlo. ¿Alguien dispuesto a echar una mano? ¿O tienes otra idea (preferiblemente más simple)?

Editar: el caso de uso aquí está depurando un enlace OpenGL (http://www.opentk.com). Tenemos que inyectar 2226 métodos con parámetros muy diferentes, por lo que es necesario un enfoque general.

Respuesta

0

Lo que finalmente terminé haciendo fue implementar mi propia solución de reescritura utilizando Mono.Cecil.Simple, rápido y funciona bien. Desafortunadamente, esto tiene que hacerse como un evento posterior a la construcción, por lo que no se trata de una inyección de código en tiempo de ejecución.

0

Nota seguro de lo que estamos tratando de hacer, pero si es simplemente redirigir el FooImplementation la que apuntaba en la Foo campo, entonces puede simplemente hacer:

class Test 
{ 
    delegate int FooDelegate(int i); 
    static FooDelegate Foo = FooImplementation; 

    static int FooImplementation(int i) 
    { 
     return i + 1; 
    } 

    public static void Main() 
    { 
     Inject(); 
     Foo(1); 
    } 


    public static void Inject() 
    { 
     var oldImpl = Foo; 

     Foo = i => 
      { 
       try 
       { 
        BeginDebug(); 
        return oldImpl(i); 
       } 
       finally 
       { 
        EndDebug(); 
       } 
      }; 
    } 

    public static void BeginDebug() 
    { 
     Console.WriteLine("Begin Foo"); 
    } 

    public static void EndDebug() 
    { 
     Console.WriteLine("End Foo"); 
    } 
} 

Por supuesto, la Inyectar no tiene por qué estar en la misma clase, sin embargo, si el campo/propiedad no es de acceso público, deberá usar Reflection para hacerlo, aunque no tan difícil.

+0

Esta solución funciona perfectamente para una cantidad manejable de métodos para inyectar. Desafortunadamente, en este caso específico, tengo que inyectar 2226 métodos con parámetros muy diferentes (esto es un enlace OpenGL, actualizaré la pregunta original para reflejar esto). –

0

¿Ha considerado usar algo como: Postsharp? http://www.sharpcrafters.com/postsharp

o política de Enterprise Library ¿Inyección? http://msdn.microsoft.com/en-us/library/ff650672.aspx

o incluso Mono.Cecil? http://www.mono-project.com/Cecil

+0

Mono.Cecil y postharp se ejecutan en tiempo de compilación, que no funcionarán aquí (tengo que ser capaz de deshabilitar/habilitar esto en tiempo de ejecución). No he enviado la Biblioteca de Enterprise antes, gracias, la verificaré. –

0

Typemock también tal vez una solución viable para el problema: http://www.typemock.com/

Usted podría también utilizar la clase RealProxy (http://msdn.microsoft.com/en-us/library/system.runtime. remoting.proxies.realproxy.aspx) para generar su propio proxy en tiempo de ejecución que primero llamará a su código inyectado y luego llamará al método real.

3

Puede usar Expressions.

var param = Expression.Parameter(typeof(int), "i"); 

Foo = Expression.Lambda(
     Expression.TryFinally(
      Expression.Block(
       <expression for DebugPrologue>, 
       Expression.Invoke(<expression for FooImplementation>, param)), 
      <expression for DebugEpilogue>), 
     param) 
     .Compile(); 

De esta manera, puede construir las expresiones para el prólogo y el epílogo en tiempo de ejecución.

+0

Gracias, muy simple y elegante. Déjame probar esta solución, ¡creo que debería funcionar! –

+0

FYI, Expresiones compiladas usan DynamicMethod detrás de las escenas. –

0

También puede probar CInject en codeplex para inyectar código en código administrado (C# o VB.NET) con gran facilidad.

Cuestiones relacionadas