Как вы вызываете метод в том же классе в IL

Если бы я писал код, это было бы очень просто. У меня есть код, который выглядит так:

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

    private void AnotherMethod(ISomeService someService)
    {
    }
}

Я могу получить определение метода для обоих методов со ссылкой на тип, но я пытаюсь получить вызов из SomeMethod в AnotherMethod, добавленный в IL, чтобы у вас был эквивалентный код, например:

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

person Dan Siegel    schedule 14.05.2020    source источник
comment
Я всегда начинаю с того, что пишу как можно больше на C#, а затем дизассемблирую, например. ILSpy с опцией IL вместо C#.   -  person Joe Sewell    schedule 14.05.2020
comment
да, сделал это ... до сих пор не ясно, что именно мне не хватает. Ближе всего я мог получить результаты в исключении декомпилятора после попытки вставить новую инструкцию. Кажется, что это должно быть очень легко сделать, но я совершенно не понимаю, чего здесь не хватает.   -  person Dan Siegel    schedule 14.05.2020


Ответы (1)


Не уверен, что это поможет, но IL из SomeMethod:

    .maxstack 8

    IL_0000: nop
    IL_0001: ret

И ИЛ от SomeMethod с 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

Sooo вы хотите добавить это в середине:

    // 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);
person Šimon Kocúrek    schedule 14.05.2020
comment
красиво... спасибо за просмотр кода IL и Mono.Cecil... это совершенно новый взгляд на код, так что я все еще пытаюсь понять его! - person Dan Siegel; 14.05.2020