动态替换C#方法的内容?

155
我想要做的是更改 C# 方法被调用时的执行方式,以便我可以编写类似于以下代码的内容:
[Distributed]
public DTask<bool> Solve(int n, DEvent<bool> callback)
{
    for (int m = 2; m < n - 1; m += 1)
        if (m % n == 0)
            return false;
    return true;
}

在运行时,我需要能够分析具有分布式属性的方法(这一点我已经可以做到),然后在函数体执行之前和函数返回之后插入代码。更重要的是,我需要在不修改调用 Solve 的代码或在函数开始处(在编译时这样做;在运行时完成是目标)的情况下实现它。
目前,我尝试了这段代码(假设 t 是存储 Solve 的类型,m 是 Solve 的 MethodInfo):
private void WrapMethod(Type t, MethodInfo m)
{
    // Generate ILasm for delegate.
    byte[] il = typeof(Dpm).GetMethod("ReplacedSolve").GetMethodBody().GetILAsByteArray();

    // Pin the bytes in the garbage collection.
    GCHandle h = GCHandle.Alloc((object)il, GCHandleType.Pinned);
    IntPtr addr = h.AddrOfPinnedObject();
    int size = il.Length;

    // Swap the method.
    MethodRental.SwapMethodBody(t, m.MetadataToken, addr, size, MethodRental.JitImmediate);
}

public DTask<bool> ReplacedSolve(int n, DEvent<bool> callback)
{
    Console.WriteLine("This was executed instead!");
    return true;
}

然而,MethodRental.SwapMethodBody仅适用于动态模块,不能使用在已编译并存储在程序集中的模块。
因此,我正在寻找一种有效的方法来对一个已存储在已加载和执行的程序集中的方法进行SwapMethodBody操作。
请注意,如果我必须将该方法完全复制到动态模块中,这也不是问题,但在这种情况下,我需要找到一种方法来复制IL,并更新所有调用Solve()的地方,使它们指向新的副本。

3
无法交换已加载的方法。否则,Spring.Net就不必使用代理和接口来实现奇怪的操作了 :-) 请阅读此问题,它与您的问题有关:https://dev59.com/KnVD5IYBdhLWcg3wTJvF(如果您可以拦截它,则可以类似于交换它... 如果您不能1,那么显然您不能2)。 - xanatos
在这种情况下,是否有一种方法可以将一个方法复制到动态模块中,并更新其余程序集,以便对该方法的调用指向新的副本? - June Rhodes
老生常谈。如果可以轻松完成,所有不同的IoC容器可能都会这样做。他们没有这样做->99%不能完成 :-)(没有可怕和无名的黑客)。有一个希望:他们承诺在C#5.0中进行元编程和异步操作。我们已经看到了异步操作...元编程还没有...但它可能是! - xanatos
1
你确实没有解释为什么想要让自己陷入这么痛苦的事情。 - DanielOfTaebl
9
请看下面我的回答。这是完全可行的。在你不拥有代码和运行时期间都可以实现。我不明白为什么会有那么多人认为这是不可能的。 - Andreas Pardeike
显示剩余4条评论
11个回答

1
请看Mono.Cecil:
using Mono.Cecil;
using Mono.Cecil.Inject;

public class Patcher
{    
   public void Patch()
   {
    // Load the assembly that contains the hook method
    AssemblyDefinition hookAssembly = AssemblyLoader.LoadAssembly("MyHookAssembly.dll");
    // Load the assembly
    AssemblyDefinition targetAssembly = AssemblyLoader.LoadAssembly("TargetAssembly.dll");

    // Get the method definition for the injection definition
    MethodDefinition myHook = hookAssembly.MainModule.GetType("HookNamespace.MyHookClass").GetMethod("MyHook");
    // Get the method definition for the injection target. 
    // Note that in this example class Bar is in the global namespace (no namespace), which is why we don't specify the namespace.
    MethodDefinition foo = targetAssembly.MainModule.GetType("Bar").GetMethod("Foo");

    // Create the injector
    InjectionDefinition injector = new InjectionDefinition(foo, myHook, InjectFlags.PassInvokingInstance | InjectFlags.passParametersVal);

    // Perform the injection with default settings (inject into the beginning before the first instruction)
    injector.Inject();

    // More injections or saving the target assembly...
   }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接