C#调用异步方法报错:无法从程序集加载类型'<deleteCommands>d__2'
解决C#异步调用中“Could not load type 'd__2' from assembly”错误
嘿,作为PHP开发者刚接触C#异步编程,确实容易踩一些框架特有的坑,我来帮你拆解这个问题~
错误原因分析
这个错误通常和异步方法编译生成的状态机类型加载失败有关,而你的代码里的async static void写法是核心诱因之一:
- C#的async方法会被编译器编译成一个包含状态机的隐藏类(比如
<deleteCommands>d__2就是这个自动生成的类名),如果方法签名是async void,编译器生成的状态机逻辑会有特殊处理,很容易因为无法被正确追踪、加载而抛出类型找不到的错误。 - 另外,
async void本身就是C#异步编程中的反模式(仅推荐用于事件处理程序),它无法被调用者等待,异常也无法被正常捕获,会进一步引发各种奇怪的运行时问题。
具体解决方案
1. 把async static void改为async static Task
这是最关键的一步,修改方法签名后,编译器会生成正确的可追踪的状态机类型,同时让调用者可以正常等待方法完成:
public async static Task DeleteCommands(List<int> commandIds) { Tebex.logWarning("Async Delete...."); // 这里不推荐用Task.Run包装同步操作,换成真正的异步IO逻辑 using var httpClient = new HttpClient(); string url = "http://www.example.com/" + "qu..."; // 补全你的URL拼接逻辑 // 用HttpClient的异步方法发起请求,替代同步包装 var response = await httpClient.GetAsync(url); // 后续可以添加响应处理逻辑,比如: if (response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(); // 处理返回内容 } }
2. 避免用Task.Run包装IO密集型操作
你原来的代码用Task.Run把HTTP请求包装成异步,这是典型的伪异步操作——它会占用线程池线程,完全浪费了异步IO的优势。直接使用HttpClient的异步方法(比如GetAsync、PostAsync)才是正确的异步IO实现。
3. 排查编译与部署问题
如果修改签名后还是报错,可以试试这些操作:
- 清理解决方案并重新编译(清除编译缓存,避免旧的状态机类残留)
- 检查项目的目标框架版本:确保使用的是.NET Framework 4.5+或.NET Core/.NET 5+,这些版本对async/await支持更完善
- 确认程序集部署完整:如果是部署到服务器/容器,检查所有依赖的程序集是否都正确上传
调用修改后的方法
修改完方法后,调用的时候也要用await来等待它完成(如果调用者本身也是async方法):
// 在另一个async方法中调用 await DeleteCommands(commandIdList);
如果是在非async的同步方法中调用,可以用.Wait()或者.GetAwaiter().GetResult()(注意死锁风险,建议尽量用async链)。
内容的提问来源于stack exchange,提问作者Liam Wiltshire
相关产品推荐
相关产品推荐

