Discord.NET:如何识别命令执行抛出的具体异常类型
解决CommandService无法识别自定义异常的问题
我之前做机器人命令系统的时候也碰到过一模一样的问题——自定义了权限不足、冷却触发这类业务异常,但CommandService.ExecuteAsync()返回的Error只会显示CommandError.Exception,没法直接区分具体异常类型。其实核心解决思路很简单,就是从执行结果的Exception属性里捞到你抛出的原始自定义异常就行。
核心方案:捕获并识别原始异常
大部分CommandService的实现会把执行过程中抛出的异常包装在返回的CommandResult对象的Exception属性里,而非直接向外抛出(除非是致命未处理错误)。所以不用在外层套try-catch,而是检查执行结果的成功状态,再针对性判断异常类型:
步骤1:给自定义异常统一基类(可选但推荐)
先给你的业务异常定义一个公共基类,后续判断起来更简洁,避免一堆零散的if-else:
// 自定义命令异常基类 public abstract class CommandBusinessException : Exception { // 给用户展示的友好提示 public abstract string UserTip { get; } } // 无权限异常 public class NoPermissionException : CommandBusinessException { public override string UserTip => "抱歉,你没有执行这个命令的权限哦"; } // 冷却触发异常 public class CommandCooldownException : CommandBusinessException { public TimeSpan RemainingTime { get; } public CommandCooldownException(TimeSpan remaining) { RemainingTime = remaining; } public override string UserTip => $"命令还在冷却中,还剩 {RemainingTime:mm\\:ss} 才能再次使用"; }
步骤2:执行后判断具体异常类型
执行命令后,先检查结果是否成功,如果失败,就从result.Exception中提取真实的自定义异常(注意部分库会把异常包装在AggregateException里,需要取内层异常或者用GetBaseException()获取最原始的异常):
var result = await _service.ExecuteAsync(context); if (!result.IsSuccess) { // 获取最内层的原始异常 var actualException = result.Exception.GetBaseException(); // 用switch判断异常类型,逻辑更清晰 switch (actualException) { case NoPermissionException noPermEx: await context.Channel.SendMessageAsync(noPermEx.UserTip); break; case CommandCooldownException cooldownEx: await context.Channel.SendMessageAsync(cooldownEx.UserTip); break; default: // 处理其他未知异常 await context.Channel.SendMessageAsync("执行命令时发生了未知错误,请稍后再试"); // 这里可以记录异常日志 _logger.LogError(actualException, "命令执行失败"); break; } }
额外提醒
- 如果你的CommandService在极端情况下会直接抛出异常,外层的try-catch还是需要保留,但大部分场景下异常都会被包装在
CommandResult里。 - 尽量避免直接捕获
Exception类型,优先判断自定义异常基类,这样不会把数据库连接错误这类非业务异常当成命令逻辑问题处理。
这样你就能继续用自定义异常统一处理权限、冷却这些场景,不用重复写判断逻辑,同时也能准确识别不同的异常类型了。
内容的提问来源于stack exchange,提问作者Orion31
相关产品推荐
相关产品推荐

