Question

Considérez le code trivial suivant:

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

Ce que je voudrais faire est inject un code de débogage dans le délégué Foo, qui serait équivalent:

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

La torsion est que je dois être en mesure de le faire à runtime , si la compilation et les méthodes de post-traitement sont hors de question.

Mon approche initiale utilisée Delegate.Combine () pour ajouter les méthodes de prologue et l'épilogue au délégué Foo. Hélas, cela ne fonctionnera pas comme munges valeurs de retour.

Mon idée actuelle est d'utiliser System.Reflection.Emit et DynamicMethod comme une solution potentielle. Pour autant que je peux dire, je dois obtenir le MethodInfo pour FooImplementation, obtenir son MethodBody, convertir en un DynamicMethod et injectez mon try-finally bloc dans cela.

Malheureusement, je ne sais absolument pas comment faire cela. Quiconque est prêt à donner un coup de main? Ou avez-vous une autre idée (de préférence plus simple)?

Edit: l'utilisation cas est le débogage d'une liaison de OpenGL ici (http://www.opentk.com). Nous devons injecter 2226 méthodes avec des paramètres très différentes, donc une approche générale est nécessaire.

Était-ce utile?

La solution 6

Qu'est-ce que j'ai finalement fini par faire était de mettre en œuvre ma propre solution de ré-écriture à l'aide Mono.Cecil. Simple, rapide et il fonctionne très bien. Malheureusement, cela doit être fait comme un événement post-construction, donc ce n'est pas réellement injection de code d'exécution.

Autres conseils

Vous pouvez utiliser des 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 cette façon, vous pouvez créer des expressions pour le prologue et l'épilogue dans l'exécution.

Notez que ce que vous essayez de faire, mais si elle est redirigeant simplement le FooImplementation qui est pointé au Foo le terrain, vous pouvez simplement faire:

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

Bien sûr, le Injecter n'a pas à être dans la même classe, si le champ / la propriété ne sont pas accessibles au public, vous devez utiliser la réflexion pour le faire, toujours pas si difficile si.

Avez-vous pensé à utiliser quelque chose comme: PostSharp? http://www.sharpcrafters.com/postsharp

ou Enterprise Library politique d'injection? http://msdn.microsoft.com/en-us/library/ff650672.aspx

ou même Mono.Cecil? http://www.mono-project.com/Cecil

Typemock aussi peut-être une solution viable à votre question: http://www.typemock.com/

Vous pouvez également utiliser la classe RealProxy (http://msdn.microsoft.com/en-us/library/system.runtime.remoting.proxies.realproxy.aspx) pour générer votre propre proxy lors de l'exécution qui va d'abord appel votre code injecté puis appeler la méthode réelle.

Vous pouvez également essayer CInject sur CodePlex au code Injecter dans le code managé (C # ou VB.NET) avec une grande facilité.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top