为何C#异步函数中局部函数抛出的异常未被触发?
问题分析与解决方案
首先,咱们来拆解下你的问题:你在InitMediaPlayer里抛出了NullReferenceException,调试器能捕获到这个异常,但应用却继续运行没有崩溃。这背后的核心原因和异步方法的异常处理机制有关。
为什么异常没有导致应用崩溃?
你的TryPlayElseStop是一个async Task<bool>方法,当它执行到await RecitationUtils.TryGetAudioFile(...)之后的代码时,这部分逻辑是在异步回调上下文里运行的。当Play方法抛出异常时,这个异常会被自动捕获并包装到TryPlayElseStop返回的Task对象中——而不是直接“冒泡”到应用的全局异常处理层。
如果调用TryPlayElseStop的地方没有await这个Task,也没有处理它的异常,那么这个异常就会变成“未被观察到的异常”。在.NET Core/.NET 5+之后的版本里,这类异常默认不会导致应用崩溃,只会在后台日志中记录(如果配置了日志的话),这就是为什么你的应用能继续运行的原因。而调试器会捕获所有抛出的异常(不管是否被处理),所以会触发断点停止。
解决办法
1. 正确await异步方法并捕获异常
这是最推荐的做法,确保你调用TryPlayElseStop时使用await,并用try-catch块捕获异常:
try { bool playSuccess = await TryPlayElseStop(targetReciterId, targetChapterId); if (playSuccess) { // 播放成功后的逻辑 } } catch (NullReferenceException ex) { // 处理初始化失败的情况,比如给用户提示 Utils.Toast($"播放失败:{ex.Message}"); }
2. 在同步场景下处理Task异常
如果因为某些限制不能用await(比如在同步方法里调用),你需要显式处理Task的异常:
TryPlayElseStop(targetReciterId, targetChapterId) .ContinueWith(task => { if (task.IsFaulted) { // 取出实际的异常(Task.Exception是AggregateException,InnerException才是真正的异常) var actualException = task.Exception?.InnerException ?? task.Exception; Utils.Toast($"播放器初始化失败:{actualException?.Message}"); } else if (task.Result) { // 播放成功的逻辑 } }, TaskScheduler.FromCurrentSynchronizationContext()); // 这里的TaskScheduler确保回调在UI线程执行,避免UI操作报错
3. 排查异常的根本原因
你提到实际异常发生在playerTimer = new MyTimer(1_000);的构造函数里,建议你先排查这个构造函数内部的逻辑:
- 是否有未初始化的依赖对象?
- 构造函数里是否访问了null引用的成员?
- 可以在调试时查看
NullReferenceException的堆栈跟踪,定位到具体的null对象。
内容的提问来源于stack exchange,提问作者Sid Go
相关产品推荐
相关产品推荐

