Pregunta

Considere el siguiente código trivial:

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 a:

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

El giro es que debo ser capaz de hacer esto en tiempo de ejecución , por lo que el tiempo de compilación y métodos de post-procesamiento están fuera de la cuestión.

Mi primera aproximación utilizada Delegate.Combine () para añadir los métodos prólogo y el epílogo al delegado Foo. Por desgracia, esto no funcionará, ya que munges valores de retorno.

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

Por desgracia, no tengo ni idea de cómo hacer esto. Cualquiera que esté dispuesto a echar una mano? O tiene otra idea (preferiblemente más simple)?

Edit: el caso de uso aquí se depura una OpenGL unión (http://www.opentk.com). Tenemos que inyectar 2226 con métodos muy diferentes parámetros, por lo que un enfoque general es necesario.

¿Fue útil?

Solución 6

Lo que finalmente terminó haciendo era poner en práctica mi propia solución reescritura usando Mono.Cecil. Sencillo, rápido y funciona bien. Por desgracia, esto tiene que ser hecho como un evento posterior a la generación, así que esto no es en realidad la inyección de código en tiempo de ejecución.

Otros consejos

Se puede usar expresiones.

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, se puede construir las expresiones para el prólogo y el epílogo en el tiempo de ejecución.

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 que estar en la misma clase, sin embargo si el campo / propiedad no es accesible al público que tendrá que utilizar la reflexión para hacerlo, todavía no se que aunque duro.

¿Usted ha considerado usar algo como: PostSharp? http://www.sharpcrafters.com/postsharp

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

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

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

También puede utilizar la clase RealProxy (http://msdn.microsoft.com/en-us/library/system.runtime.remoting.proxies.realproxy.aspx) para generar su propia representación en tiempo de ejecución que será la primera llamada su código inyectado y luego llamar al método actual.

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top