ILGenerator: Как создать Fun c <> для передачи в качестве аргумента - PullRequest
4 голосов
/ 07 апреля 2020

Я пытаюсь создать прокси-оболочку для сервисов, которые определяются интерфейсом, чтобы «скрыть» клиентский указанный c код вызова от пользователя-разработчика сервиса.

Я уже смог для создания большей части самого класса-оболочки, но сейчас я пытаюсь сгенерировать аргумент Fun c для уже существующего клиентского вызова.

Пример TService имеет интерфейс:

public interface ITestService
{
    public Task ServiceMethodAsync();
    public Task<TestDTO> ServiceMethodWithResultAsync();
}

И подпись для реализации клиента:

public Task<TResult> CallAsync<TResult>(Func<TService, Task<TResult>> operation);
public Task CallAsync(Func<TService, Task> operation);

. При непосредственном вызове разработчик в настоящее время пишет что-то вроде:

var result = await client.CallAsync(service => service.ServiceMethodWithResultAsync(requestDTO));

Хотя предпочтительным вариантом будет то, что он может просто вызвать:

var result = await service.ServiceMethodWithResultAsync(requestDTO);

, поскольку это позволяет либо локальному сервису, либо моей удаленной оболочке быть введенным DI с использованием интерфейса IService, а также то, является ли это удаленным сервисом или локальным сервисом, разработчик не должен учитывать.

В настоящее время мой типообразователь генерирует необходимый код для создания класса-оболочки, наследуемого от кли nt baseType и определение всех методов интерфейса для него, но в настоящее время передача значения NULL как Func<TService, Task<TResult>> operation.

private static void DefineInterface(TypeBuilder typeBuilder, Type baseType, Type serviceInterfaceType)
{
    foreach (MethodInfo serviceMethod in serviceInterfaceType.GetMethods())
    {
        ParameterInfo[] parameters = serviceMethod.GetParameters();
        MethodBuilder methodBuilder = typeBuilder.DefineMethod(
            serviceMethod.Name,
            serviceMethod.Attributes ^ MethodAttributes.Abstract,
            serviceMethod.CallingConvention,
            serviceMethod.ReturnType,
            serviceMethod.ReturnParameter.GetRequiredCustomModifiers(),
            serviceMethod.ReturnParameter.GetOptionalCustomModifiers(),
            parameters.Select(p => p.ParameterType).ToArray(),
            parameters.Select(p => p.GetRequiredCustomModifiers()).ToArray(),
            parameters.Select(p => p.GetOptionalCustomModifiers()).ToArray());

        ILGenerator ilGenerator = methodBuilder.GetILGenerator();

        MethodInfo callAsyncMethod = getCallAsyncMethod(baseType, serviceMethod);

        ilGenerator.Emit(OpCodes.Ldarg_0); // Push "this"
        ilGenerator.Emit(OpCodes.Ldnull); // Push Func<> argument
        ilGenerator.Emit(OpCodes.Call, callAsyncMethod); // Call CallAsync on the base client
        ilGenerator.Emit(OpCodes.Ret);
    }
}

Отсюда я немного застрял на том, как именно go об определении Fun c <> service => service.ServiceMethodWithResultAsync(requestDTO), включая ожидаемые / предполагаемые параметры requestDTO, будут в Ldarg_1, Ldarg_2 et c. ?

Любая и вся помощь очень ценится.

...