使用DynamicMethod注入运行时代码?

时间:2010-12-04 21:15:30

标签: c# code-injection reflection.emit dynamicmethod opentk

考虑以下简单代码:

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

我想做的是将一些调试代码注入到Foo委托中,这相当于:

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

我必须能够在运行时中执行此操作,因此编译时和后处理方法是不可能的。

我的初始方法使用Delegate.Combine()将序言和结尾方法添加到Foo委托。唉,这不会起作用,因为它会回报价值。

我目前的想法是使用System.Reflection.Emit和DynamicMethod作为潜在的解决方案。据我所知,我需要为FooImplementation获取MethodInfo,获取它的MethodBody,将其转换为DynamicMethod并将try-finally块注入其中。

不幸的是,我完全不知道该怎么做。谁愿意伸出援助之手?或者你有另一个(最好是更简单的)想法吗?

编辑:这里的用例是调试OpenGL绑定(http://www.opentk.com)。我们必须注入2226种具有完全不同参数的方法,因此需要采用一般方法。

6 个答案:

答案 0 :(得分:3)

您可以使用表达式。

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

这样,您可以在运行时为序言和结尾构建表达式。

答案 1 :(得分:0)

请注意确定您要执行的操作,但如果只是重定向FooImplementation字段中指向的Foo,则可以执行以下操作:

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

当然,Inject不必属于同一个类,但是如果字段/属性不可公开访问,则必须使用Reflection来执行此操作,但仍然不是那么难。

答案 2 :(得分:0)

您是否考虑过使用以下内容: Postsharp? http://www.sharpcrafters.com/postsharp

或企业库政策注入? http://msdn.microsoft.com/en-us/library/ff650672.aspx

甚至是Mono.Cecil? http://www.mono-project.com/Cecil

答案 3 :(得分:0)

TypeMock也可能是解决您问题的可行方案: http://www.typemock.com/

您还可以使用RealProxy类(http://msdn.microsoft.com/en-us/library/system.runtime.remoting.proxies.realproxy.aspx)在运行时生成您自己的代理,该代理将首先调用你注入的代码,然后调用实际的方法。

答案 4 :(得分:0)

您还可以在codeplex上试用CInject,以便轻松地将代码注入托管代码(C#或VB.NET)。

答案 5 :(得分:0)

我最终做的是使用Mono.Cecil实现我自己的重写解决方案。简单,快速,工作正常。不幸的是,这必须作为一个构建后事件来完成,所以这实际上不是运行时代码注入。