Domanda

Si consideri il seguente codice banale:

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);
    }
}

Quello che vorrei fare è iniettare un po 'di codice di debug nella delegato Foo, che sarebbe equivalente:

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

La torsione è che devo essere in grado di farlo a runtime , quindi in fase di compilazione e di metodi di post-processing sono fuori questione.

Il mio primo approccio utilizzato Delegate.Combine () per aggiungere i metodi di prologo ed epilogo al delegato Foo. Purtroppo, questo non funzionerà come munges valori restituiti.

La mia idea attuale è quella di utilizzare System.Reflection.Emit e DynamicMethod come una potenziale soluzione. Per quanto posso dire, ho bisogno di ottenere il MethodInfo per FooImplementation, ottenere il suo MethodBody, convertito che ad un DynamicMethod e iniettare il try-blocco finally in quello.

Purtroppo, non ho assolutamente idea di come fare questo. Chiunque abbia voglia di dare una mano? O avete un'altra idea (preferibilmente più semplice)?

Edit: il caso d'uso qui è il debug di un OpenGL vincolante (http://www.opentk.com). Dobbiamo iniettare 2226 metodi con parametri molto diversi, quindi un approccio generale è necessario.

È stato utile?

Soluzione 6

Quello che ho finalmente finito per fare è stato quello di implementare la mia soluzione riscrittura utilizzando Mono.Cecil. Semplice, veloce e funziona bene. Purtroppo, questo deve essere fatto come un evento post-generazione, quindi questo non è in realtà l'inserimento di codice runtime.

Altri suggerimenti

È possibile utilizzare le espressioni.

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();

In questo modo, è possibile costruire le espressioni per il prologo ed epilogo in fase di esecuzione.

Nota sicuro di quello che stai cercando di fare, ma se è semplicemente reindirizzando l'FooImplementation che ha sottolineato al Foo Field allora si può semplicemente fare:

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");
    }
}

Naturalmente l'Inject non deve essere nella stessa classe, se il campo / proprietà non è accessibile al pubblico si dovrà utilizzare la reflection per farlo, ancora non è che anche se è difficile.

Hai pensato di usare qualcosa di simile a: PostSharp? http://www.sharpcrafters.com/postsharp

o politica iniezione Enterprise Library? http://msdn.microsoft.com/en-us/library/ff650672.aspx

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

TypeMock anche forse una soluzione praticabile al problema in questione: http://www.typemock.com/

Si potrebbe anche utilizzare la classe RealProxy (http://msdn.microsoft.com/en-us/library/system.runtime.remoting.proxies.realproxy.aspx) per generare la propria delega in fase di esecuzione che sarà prima chiamata il codice iniettato e quindi chiamare il metodo effettivo.

È anche possibile provare CInject su CodePlex in codice iniettare in codice gestito (C # o VB.NET) con grande facilità.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top