如何在 IL 中的同一个 class 中调用方法

How do you invoke a method within the same class in IL

如果我正在编写代码,这将非常简单。我的代码看起来像:

public class SomeClass
{
    public void SomeMethod(ISomeService someService)
    {
    }

    private void AnotherMethod(ISomeService someService)
    {
    }
}

我可以通过类型引用获取这两种方法的方法定义,但我想要获取的是从 SomeMethod 到在 IL 中添加的 AnotherMethod 的调用,因此您将拥有类似的等效代码:

public void SomeMethod(ISomeService someService)
{
    AnotherMethod(someService);
}

我现在完全迷失了试图理解如何正确构建必要的指令。

我现在拥有的看起来像:

private void ProcessType(TypeDefinition typeDef)
{
    var anotherMethodDef = typeDef.Methods.FirstOrDefault(x => HasMethod(x, "AnotherMethod"));
    if(someMethodDef != null)
    {
        var someMethodDef = typeDef.Methods.First(x => HasMethod(x, "SomeMethod"));
        var processor = someMethodDef.Body.GetILProcessor();

        // Now I need to generate:
        // AnotherMethod(someService); as a new instruction
    }
}

private static bool HasMethod(MethodDefinition method, string expected) =>
    method.Name == expected && method.Parameters.Count() == 1 &&
        method.Parameters.First().TypeDefinition.FullName == "Contoso.ISomeService";

不确定这是否有帮助,但是 SomeMethod:

    .maxstack 8

    IL_0000: nop
    IL_0001: ret

SomeMethod 的 IL 和 AnotherMethod(someService);

    .maxstack 8

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldarg.1
    IL_0003: call instance void SomeClass::AnotherMethod(class [YourAssembly]YourNamespace.ISomeService)
    IL_0008: nop
    IL_0009: ret

所以你想在中间添加这个:

    // Add breakpoint space (
    IL_0000: nop
    // Load `this` (
    IL_0001: ldarg.0
    // Load your first argument... which is `someService`
    IL_0002: ldarg.1
    // Call method with this signature and previously loaded arguments
    IL_0003: call instance void SomeClass::AnotherMethod(class [YourAssembly]YourNamespace.ISomeService)

最后这应该可以解决问题:

var myAssembly = Assembly.GetAssembly(typeof(SomeClass));
var module = myAssembly.MainModule;
var anotherMethodRef = module.Import(typeof(SomeClass).GetMethod("AnotherMethod", new[] { typeof(ISomeService) }));

var nop = processor.Create(OpCodes.Nop);
var loadThis = processor.Create(OpCodes.Ldarg_0);
var loadSomeService = processor.Create(OpCodes.Ldarg_1);
var callMethod = processor.Create(OpCodes.Call, anotherMethodRef);

// First instruction was IL_0000: nop
var firstInstruction = someMethodDef.Body.Instructions[0];

processor.InsertBefore(firstInstruction, nop);
processor.InsertBefore(firstInstruction, loadThis);
processor.InsertBefore(firstInstruction, loadSomeService);
processor.InsertBefore(firstInstruction, callMethod);