【问题标题】:Mono Cecil get delegate method argumentMono Cecil 获取委托方法参数
【发布时间】:2015-07-30 22:03:02
【问题描述】:

我在下面有一个非常简单的组件。我正在尝试使用 Mono.Cecil 对其进行反思,以找到传递给所有 CallApiAsync 调用的参数。当我 隔离调用的 MethodReference 我似乎无法获得参数 x.GetResponse(new SomeRequestType()),我只获得了委托定义 ApiMethodAsync<T, TResult>。我对此一无所知,任何帮助表示赞赏。

public class ApiWrapper
{
 public delegate Task<TResult> ApiMethodAsync<T, TResult>(T api);

 public virtual async Task<SomeResponseType> MakeSomeRequestToApi() 
 {
    return await CallApiAsync<ISomeApi, SomeResponseType>(x => x.GetResponse(new SomeRequestType()));
 }

 public virtual async Task<TResult> CallApiAsync<T, TResult>(ApiMethodAsync<T, TResult> apiMethod) where TResult : new()
 {
    return await Task.FromResult(new TResult());
 }
}

public interface ISomeApi
{
    Task<SomeResponseType> GetResponse(SomeRequestType request);
}

public class SomeResponseType { }
public class SomeRequestType { }

下面是我用来识别对 CallApiAsync 的调用的 Mono Cecil 代码

var moduleDefinition = ModuleDefinition.ReadModule("SimpleAssembly.dll");

var targetClass = moduleDefinition.Types.Where(t => t.FullName.Contains("ApiWrapper")).Single();

            var nestedMethodInstructions = targetClass.NestedTypes
                                                      .SelectMany(p => p.Methods)
                                                      .Where(m => m.HasBody)
                                                      .SelectMany(t => t.Body.Instructions).ToList();

            foreach (var instr in nestedMethodInstructions)
            {
                if (instr.Operand != null)
                {
                    var methodRef = instr.Operand as MethodReference;

                    if (methodRef != null && methodRef.FullName.Contains("CallApiAsync"))
                    {
                        // Get the full delegate parameter, ie  GetResponse(new SomeRequestType())
                    }
                }
            }

【问题讨论】:

  • 所以让我直说吧;你想拦截对CallApiAsync方法的所有调用并保存传递给它的参数吗?
  • 是的,我想捕获通用参数和作为参数传入的 lambda 表达式

标签: reflection delegates mono.cecil


【解决方案1】:

关于如何做你想做的事,我会有线索,但我还没有设法做到。问题是,async/await 是一种语法糖,因此您的 MakeSomeRequestToApiCallApi 方法都被编译器扩展为以下代码(MakeSomeRequestToApi 和 CallApi 看起来很漂亮差不多):

[CompilerGenerated]
[StructLayout(LayoutKind.Auto)]
private struct <MakeSomeRequestToApi>d__2 : IAsyncStateMachine
{
        public int <>1__state;

        public AsyncTaskMethodBuilder<SomeResponseType> <>t__builder;

        public ApiWrapper <>4__this;

        private TaskAwaiter<SomeResponseType> <>u__$awaiter3;

        private object <>t__stack;

        void IAsyncStateMachine.MoveNext()
        {
            SomeResponseType result;
            try
            {
                int num = this.<>1__state;
                if (num != -3)
                {
                    TaskAwaiter<SomeResponseType> taskAwaiter;
                    if (num != 0)
                    {
                        ApiWrapper arg_43_0 = this.<>4__this;
                        if (ApiWrapper.CS$<>9__CachedAnonymousMethodDelegate1 == null)
                        {
                            ApiWrapper.CS$<>9__CachedAnonymousMethodDelegate1 = new ApiWrapper.ApiMethodAsync<ISomeApi, SomeResponseType>(ApiWrapper.<MakeSomeRequestToApi>b__0);
                        }
                        taskAwaiter = arg_43_0.CallApiAsync<ISomeApi, SomeResponseType>(ApiWrapper.CS$<>9__CachedAnonymousMethodDelegate1).GetAwaiter();
                        if (!taskAwaiter.IsCompleted)
                        {
                            this.<>1__state = 0;
                            this.<>u__$awaiter3 = taskAwaiter;
                            this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<SomeResponseType>, ApiWrapper.<MakeSomeRequestToApi>d__2>(ref taskAwaiter, ref this);
                            return;
                        }
                    }
                    else
                    {
                        taskAwaiter = this.<>u__$awaiter3;
                        this.<>u__$awaiter3 = default(TaskAwaiter<SomeResponseType>);
                        this.<>1__state = -1;
                    }
                    SomeResponseType arg_A8_0 = taskAwaiter.GetResult();
                    taskAwaiter = default(TaskAwaiter<SomeResponseType>);
                    result = arg_A8_0;
                }
            }
            catch (Exception exception)
            {
                this.<>1__state = -2;
                this.<>t__builder.SetException(exception);
                return;
            }
            this.<>1__state = -2;
            this.<>t__builder.SetResult(result);
        }

        [DebuggerHidden]
        void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0)
        {
            this.<>t__builder.SetStateMachine(param0);
        }
    }

结构中间有一条线

ApiWrapper.CS$<>9__CachedAnonymousMethodDelegate1 = new ApiWrapper.ApiMethodAsync<ISomeApi, SomeResponseType>(ApiWrapper.<MakeSomeRequestToApi>b__0);

在源代码中是这样的:

CallApiAsync<ISomeApi, SomeResponseType>(x => x.GetResponse(new SomeRequestType()));

而在构造函数中传入的参数,在一行的末尾是:

[CompilerGenerated]
    private static Task<SomeResponseType> <MakeSomeRequestToApi>b__0(ISomeApi x)
    {
        return x.GetResponse(new SomeRequestType());
    }

因此,总而言之,我确信可以在操作数和指令中查找所有这些内容,但是 async/await 和委托使它成为一项工作。希望我能帮上一点忙。

附: 该代码已被 ILSpy 工具反编译,“反编译异步方法”选项已关闭

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-29
    • 1970-01-01
    • 2015-04-22
    • 2015-12-17
    • 2011-03-12
    相关资源
    最近更新 更多