CQL 教程/CQL 进阶/异常处理

异常处理

使用尝试、捕获、否则、最终与抛出结构化地处理程序错误,并学会自定义异常与异常链。

14.1尝试 与 捕获

尝试 包裹可能出错的代码,用 捕获 异常类型 作为 e 拦截异常并访问异常对象:

cql
# 尝试 与 捕获
尝试:
    打印(10 / 0)
捕获 异常 作为 e:
    打印("出错了:", e)

捕获 异常 会捕获所有异常;也可以一次写多个 捕获 分支处理不同类型的错误(对应 Python 的多分支 except)。

14.2捕获 具体异常

CQL 内置了常见异常的中文名,例如 值错误类型错误键错误索引错误除零错误运行时错误 等:

cql
# 捕获具体异常
尝试:
    值 = 整数("不是数字")
捕获 值错误 作为 e:
    打印("值错误:", e)

14.3否则 与 最终

否则 块只在没有异常时执行(对应 Python 的 try-else);最终 块无论是否发生异常都会执行(对应 finally):

cql
# 否则 与 最终
尝试:
    打印(10 / 2)
否则:
    打印("没有异常,执行否则块")
最终:
    打印("无论是否异常都会执行")
out
5.0
没有异常,执行否则块
无论是否异常都会执行

14.4抛出

抛出 异常类型("消息") 主动抛出异常,中断当前执行并向上层传递:

cql
# 抛出
函数 检查(分数):
    如果 分数 < 60:
        抛出 值错误("不及格:" + 文本(分数))
    返回 "通过"

尝试:
    检查(45)
捕获 值错误 作为 e:
    打印("捕获到:", e)

14.5自定义异常

继承 异常 类即可定义自己的异常,用 跳过 保持类体为空:

cql
# 自定义异常
类 余额不足错误 继承 异常:
    跳过

尝试:
    抛出 余额不足错误("余额不足")
捕获 余额不足错误 作为 e:
    打印("业务异常:", e)

14.6异常链

抛出 ... 从 e 在捕获异常后抛出新的异常,并保留原始异常作为原因(对应 Python 的 raise ... from e):

cql
# 异常链
尝试:
    尝试:
        1 / 0
    捕获 除零错误 作为 e:
        抛出 运行时错误("计算失败") 从 e
捕获 运行时错误 作为 e:
    打印("外层捕获:", e)
    打印("原因:", e.__cause__)

下面是 examples/exception.cql 的银行账户取款完整示例,它综合运用了自定义异常、抛出、否则与最终:

cql
# CQL 示例:银行账户取款
类 余额不足错误 继承 异常:
    跳过

类 银行账户:
    函数 __init__(自身, 初始余额):
        自身.余额 = 初始余额

    函数 取款(自身, 金额):
        如果 金额 > 自身.余额:
            抛出 余额不足错误("余额不足:需要 {},仅有 {}".format(金额, 自身.余额))
        自身.余额 -= 金额
        返回 自身.余额

账户 = 银行账户(100)
尝试:
    账户.取款(150)
捕获 余额不足错误 作为 e:
    打印("捕获到异常:", e)
否则:
    打印("取款成功")
最终:
    打印("当前余额:", 账户.余额)
out
捕获到异常: 余额不足:需要 150,仅有 100
当前余额: 100