如何调试自动生成代码?调试IAsyncStateMachine的MoveNext方法
调试IAsyncStateMachine.MoveNext的可行方法
没问题,我来给你几个实用的方案,帮你实打实调试IAsyncStateMachine的MoveNext方法,彻底摸清它的内部变量和执行逻辑:
方法一:用Visual Studio原生调试(最推荐)
这是最顺手的方式,只要稍微调整下调试设置就能深入到状态机内部:
- 先开启显示所有框架代码:打开「工具」→「选项」→「调试」→「常规」,取消勾选「启用仅我的代码」,同时勾选「显示所有框架代码」。这样就能看到编译器生成的状态机类了。
- 定位状态机类:编译器会把你的异步方法自动生成一个实现
IAsyncStateMachine的私有类,命名格式一般是<你的异步方法名>d__X(X是数字,比如<GetDataAsync>d__3)。你可以先用ILSpy找到这个类的名字,再回到Visual Studio,通过「编辑」→「查找和替换」→「查找符号」直接搜这个类名。 - 给
MoveNext加断点:找到状态机类里的MoveNext方法,直接打个断点。调试时,当异步方法执行到对应阶段,就会自动断在这里,你可以查看局部变量、调用栈,甚至逐行步进观察每一步的执行。 - 进阶技巧:如果不想每次都断住,可以给断点加条件或者设置命中次数,精准触发你关心的执行分支。
方法二:用dnSpy集成式调试
如果你觉得ILSpy的调试能力不够,dnSpy是更好的选择——它把反编译和调试无缝整合了:
- 加载你的程序集后,直接找到编译器生成的状态机类(同样是
<方法名>d__X格式),定位到MoveNext方法。 - 直接在反编译的代码上打断点,启动调试。当异步方法运行到对应状态时,会自动断在
MoveNext内部,你可以查看所有私有字段(状态机的变量都存在这里)、寄存器值,甚至临时修改变量来测试不同分支的执行结果。 - 它还支持即时编辑反编译代码并重新运行,调试起来非常灵活。
方法三:手动反射输出状态机信息(临时排查用)
如果不想用调试工具,也可以通过反射直接读取状态机的内部信息:
- 异步方法都会带有
AsyncStateMachineAttribute,你可以通过这个属性获取状态机的类型:// 假设你的异步方法叫YourAsyncMethod,所在类是YourClass var asyncMethod = typeof(YourClass).GetMethod("YourAsyncMethod"); var stateMachineAttr = asyncMethod.GetCustomAttribute<AsyncStateMachineAttribute>(); var stateMachineType = stateMachineAttr.StateMachineType; - 如果你能拿到状态机的实例(比如在异步方法内部通过一些技巧获取,或者用调试器附加后抓取),就可以遍历它的私有字段,查看所有内部变量:
这里的字段比如foreach (var field in stateMachineType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic)) { Console.WriteLine($"字段名:{field.Name},值:{field.GetValue(stateMachineInstance)}"); }<>1__state表示当前状态机的执行阶段,<>t__builder是方法构建器,你的局部变量会被命名为<>u__1、<>u__2这类格式。
额外注意事项
- 状态机的字段都是编译器自动生成的,命名规则固定,不同的
<>1__state值对应异步方法的不同执行阶段(比如0表示初始状态,1表示等待后的恢复状态)。 - 异步方法的
MoveNext可能在不同线程上执行,调试时要注意线程切换,避免漏掉关键执行步骤。 - 如果是.NET Core/.NET 5+版本,记得开启符号下载(「工具」→「选项」→「调试」→「符号」,勾选Microsoft符号服务器),这样能看到更完整的调试信息。
内容的提问来源于stack exchange,提问作者Bercovici Adrian
相关产品推荐
相关产品推荐

