6

考虑以下简单代码:

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() 将 prologue 和 epilogue 方法添加到 Foo 委托。唉,这行不通,因为它会处理返回值。

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

不幸的是,我完全不知道如何做到这一点。有人愿意伸出援手吗?还是您有另一个(最好更简单)的想法?

编辑:这里的用例是调试 OpenGL 绑定 (http://www.opentk.com)。我们必须注入 2226 个参数差异很大的方法,因此需要一个通用的方法。

4

6 回答 6

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

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

于 2010-12-05T00:16:45.490 回答
0

我最终做的是使用 Mono.Cecil 实现我自己的重写解决方案。简单,快速,而且效果很好。不幸的是,这必须作为构建后事件来完成,因此这实际上不是运行时代码注入。

于 2013-12-24T15:46:13.050 回答
0

请注意确定您要执行的操作,但如果它只是重​​定向FooImplementation指向FooField 的内容,那么您可以简单地执行以下操作:

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 来做到这一点,但仍然不是那么难。

于 2010-12-04T21:54:30.867 回答
0

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

于 2012-09-03T05:12:41.287 回答
0

您是否考虑过使用类似的东西:Postsharp? http://www.sharpcrafters.com/postsharp

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

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

于 2010-12-04T22:26:23.083 回答
0

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

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

于 2010-12-04T22:36:36.417 回答