Mono.Cecil:获取作为泛型参数传入的委托的方法引用
核心原因
你看到的!!0是编译器生成的闭包类占位符——Lambda表达式作为委托参数传递时,会被编译器自动转换成匿名委托,逻辑会封装到一个自动生成的私有类方法里,而非直接内联在AddSingleton调用的代码位置。所以直接查看AddSingleton调用的IL指令,看不到MongoDBContext的引用,因为委托本身指向的是那个生成类的方法。
具体解决步骤
定位委托的目标方法
找到AddSingleton调用的第二个参数(即Func委托),它本质是System.Func<IServiceProvider, IMongoDBContext>类型的实例。在IL中,这个参数通过ldftn(加载函数指针)或newobj创建,你需要追踪这个委托指向的实际方法。这类方法通常是编译器生成的,名称类似<Bar>b__0_0,所属类格式为包含Lambda的类+<>c__DisplayClassX_Y(比如Foo+<>c__DisplayClass0_0)。解析闭包类和目标方法
通过Mono.Cecil找到生成的方法后,分析该方法的IL指令,就能看到调用MongoDBContext构造函数的指令(比如newobj instance void MongoDBContext::.ctor(string))。可以通过方法的DeclaringType属性找到对应的自动生成闭包类。泛型参数解析
!!0对应AddSingleton<TService>中的泛型参数TService(也就是IMongoDBContext),需要通过MethodReference的GenericArguments属性解析这个泛型参数的实际类型,再结合委托的返回类型验证目标方法的返回值是否匹配。
Mono.Cecil操作片段示例
// 假设已定位到AddSingleton的MethodCallInstruction var addSingletonCall = ...; // 获取委托参数对应的MethodReference var delegateArg = addSingletonCall.Operand as MethodReference; if (delegateArg != null && delegateArg.DeclaringType.FullName.StartsWith("System.Func`2")) { // 回溯找到创建委托的指令(ldftn或newobj) var instruction = addSingletonCall.Previous; while (instruction != null && instruction.OpCode != OpCodes.Ldftn && instruction.OpCode != OpCodes.Newobj) { instruction = instruction.Previous; } if (instruction?.Operand is MethodReference targetMethod) { // targetMethod就是Lambda对应的实际方法 Console.WriteLine($"Lambda对应方法: {targetMethod.FullName}"); // 解析该方法的IL指令 var methodBody = targetMethod.Resolve().Body; foreach (var ilInstr in methodBody.Instructions) { // 查找MongoDBContext的构造函数调用 if (ilInstr.OpCode == OpCodes.Newobj && ilInstr.Operand is MethodReference ctor) { if (ctor.DeclaringType.Name == "MongoDBContext") { Console.WriteLine("找到目标构造函数: " + ctor.FullName); } } } } }
注意事项
- 编译器优化会影响闭包类生成:如果Lambda没有捕获外部变量,部分编译器会将方法生成在包含类的静态方法中,而非闭包类,但核心思路仍是追踪委托的目标方法。
- 泛型方法解析需注意:
AddSingleton是泛型扩展方法,要先解析它的GenericArguments得到TService的实际类型,再对应委托的类型参数。
内容的提问来源于stack exchange,提问作者AttentionDeficient

