【问题标题】:How do you invoke a method within the same class in IL如何在 IL 中调用同一类中的方法
【发布时间】:2020-05-14 19:42:17
【问题描述】:

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

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

    private void AnotherMethod(ISomeService someService)
    {
    }
}

我可以使用 Type 引用获取这两种方法的 Method 定义,但我想要获取的是从 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";

【问题讨论】:

  • 我总是从尽可能多地用 C# 编写开始,然后在例如ILSpy 使用“IL”选项而不是“C#”。
  • 是的,这样做了......仍然不清楚我到底错过了什么。在尝试插入新指令后,我最接近的结果是反编译器异常。这似乎应该很容易做到,但我完全失去了我在这里所缺少的东西。

标签: c# mono.cecil


【解决方案1】:

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

    .maxstack 8

    IL_0000: nop
    IL_0001: ret

SomeMethodAnotherMethod(someService); 的 IL

    .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 (https://stackoverflow.com/questions/4590382/why-does-generated-il-code-start-with-a-nop)
    IL_0000: nop
    // Load `this` (https://stackoverflow.com/questions/1785372/why-do-i-have-to-do-ldarg-0-before-calling-a-field-in-msil)
    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);

【讨论】:

  • 漂亮...感谢 IL 和 Mono.Cecil 代码的演练。这是查看代码的全新方式,所以我仍在努力解决它!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-26
  • 2011-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-17
  • 2021-04-08
相关资源
最近更新 更多